Refactor: перенос API загрузки файлов в приложение api

- Создан api/v1/communications/views.py с функцией upload_message_with_attachments
- Добавлен endpoint /api/v1/communications/upload-message/
- Обновлён frontend для использования правильного API пути
- Удалён дублирующий код из communications/api_views.py
- Обновлён communications/urls.py (убран старый endpoint)

Теперь все API эндпоинты находятся в приложении api, как и должно быть
This commit is contained in:
admin i 2025-11-28 07:48:47 +03:00
parent d7139f7f84
commit cc71038c5e
6 changed files with 124 additions and 104 deletions

View File

@ -0,0 +1 @@
# backend/api/v1/communications/__init__.py

View File

@ -0,0 +1,113 @@
# backend/api/v1/communications/views.py
"""API views для чатов и сообщений"""
from django.contrib.auth.decorators import login_required
from django.db import transaction
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from django.views.decorators.http import require_POST
from communications.models import (
Chat,
ChatMembership,
Message,
MessageAttachment,
)
@login_required
@require_POST
def upload_message_with_attachments(request):
"""Отправка сообщения с вложениями"""
chat_id = request.POST.get('chat_id')
content = request.POST.get('content', '').strip()
if not chat_id:
return JsonResponse(
{'ok': False, 'error': 'chat_id required'},
status=400
)
chat = get_object_or_404(Chat, pk=chat_id)
# Проверка доступа к чату
membership = ChatMembership.objects.filter(
chat=chat,
user=request.user,
is_active=True,
can_send_messages=True
).first()
if not membership:
return JsonResponse(
{'ok': False, 'error': 'Cannot send messages to this chat'},
status=403
)
# Если нет ни текста, ни файлов
if not content and not request.FILES:
return JsonResponse(
{'ok': False, 'error': 'Content or files required'},
status=400
)
with transaction.atomic():
# Создаем сообщение
message = Message.objects.create(
chat=chat,
author=request.user,
content=content or '',
has_attachments=bool(request.FILES)
)
# Обрабатываем все загруженные файлы
attachments = []
for file_key in request.FILES:
uploaded_file = request.FILES[file_key]
# Определение типа файла
mime_type = uploaded_file.content_type or ''
if mime_type.startswith('image/'):
file_type = 'image'
elif mime_type.startswith('video/'):
file_type = 'video'
elif mime_type.startswith('audio/'):
file_type = 'audio'
else:
file_type = 'file'
attachment = MessageAttachment.objects.create(
message=message,
file=uploaded_file,
file_type=file_type,
file_name=uploaded_file.name,
file_size=uploaded_file.size,
mime_type=mime_type
)
attachments.append({
'id': attachment.id,
'file_name': attachment.file_name,
'file_type': attachment.file_type,
'file_url': attachment.file.url,
'file_size': attachment.file_size,
'mime_type': attachment.mime_type
})
avatar_url = None
if hasattr(request.user, 'avatar') and request.user.avatar:
avatar_url = request.user.avatar.url
return JsonResponse({
'ok': True,
'message_id': message.id,
'chat_id': chat.id,
'author': {
'id': request.user.id,
'full_name': request.user.get_full_name(),
'avatar': avatar_url
},
'content': message.content,
'created_at': message.created_at.isoformat(),
'attachments': attachments
})

View File

@ -10,6 +10,7 @@ from .employees.views import (DepartmentRoleViewSet, DepartmentViewSet,
from .feed.views import CommentViewSet, PostViewSet
from .documents.views import DocumentViewSet
from .requests_app.views import RequestViewSet
from .communications.views import upload_message_with_attachments
app_name = "v1"
@ -36,5 +37,10 @@ urlpatterns = [
path("auth/register/", RegisterAPIView.as_view(), name="register"),
path("auth/resend-email/", ResendEmailAPIView.as_view(), name="resend-email"),
path("auth/verify-email/", VerifyEmailAPIView.as_view(), name="verify-email"),
path(
"communications/upload-message/",
upload_message_with_attachments,
name="upload_message"
),
path("", include(router.urls)),
]

View File

@ -306,104 +306,8 @@ def pin_message(request, message_id):
# ============ ATTACHMENTS ============
@login_required
@require_POST
def upload_message_with_attachments(request):
"""Отправка сообщения с вложениями"""
chat_id = request.POST.get('chat_id')
content = request.POST.get('content', '').strip()
if not chat_id:
return JsonResponse(
{'ok': False, 'error': 'chat_id required'},
status=400
)
chat = get_object_or_404(Chat, pk=chat_id)
# Проверка доступа к чату
membership = ChatMembership.objects.filter(
chat=chat,
user=request.user,
is_active=True,
can_send_messages=True
).first()
if not membership:
return JsonResponse(
{'ok': False, 'error': 'Cannot send messages to this chat'},
status=403
)
# Если нет ни текста, ни файлов
if not content and not request.FILES:
return JsonResponse(
{'ok': False, 'error': 'Content or files required'},
status=400
)
with transaction.atomic():
# Создаем сообщение
message = Message.objects.create(
chat=chat,
author=request.user,
content=content or '',
has_attachments=bool(request.FILES)
)
# Обрабатываем все загруженные файлы
attachments = []
for file_key in request.FILES:
uploaded_file = request.FILES[file_key]
# Определение типа файла
mime_type = uploaded_file.content_type or ''
if mime_type.startswith('image/'):
file_type = 'image'
elif mime_type.startswith('video/'):
file_type = 'video'
elif mime_type.startswith('audio/'):
file_type = 'audio'
else:
file_type = 'file'
attachment = MessageAttachment.objects.create(
message=message,
file=uploaded_file,
file_type=file_type,
file_name=uploaded_file.name,
file_size=uploaded_file.size,
mime_type=mime_type
)
attachments.append({
'id': attachment.id,
'file_name': attachment.file_name,
'file_type': attachment.file_type,
'file_url': attachment.file.url,
'file_size': attachment.file_size,
'mime_type': attachment.mime_type
})
avatar_url = None
if hasattr(request.user, 'avatar') and request.user.avatar:
avatar_url = request.user.avatar.url
return JsonResponse({
'ok': True,
'message_id': message.id,
'chat_id': chat.id,
'author': {
'id': request.user.id,
'full_name': request.user.get_full_name(),
'avatar': avatar_url
},
'content': message.content,
'created_at': message.created_at.isoformat(),
'attachments': attachments
})
# Примечание: endpoint для загрузки сообщений с файлами
# перенесён в api/v1/communications/views.py
@login_required
@require_POST

View File

@ -69,11 +69,7 @@ urlpatterns = [
),
# Attachments
path(
"api/upload-message/",
api_views.upload_message_with_attachments,
name="api_upload_message"
),
# Основной endpoint для загрузки сообщений с файлами находится в api/v1
path(
"api/attachment/upload/",
api_views.upload_attachment,

View File

@ -344,7 +344,7 @@ document.addEventListener('DOMContentLoaded', function() {
formData.append('chat_id', '{{ chat.id }}');
try {
const response = await fetch('/communications/api/upload-message/', {
const response = await fetch('/api/v1/communications/upload-message/', {
method: 'POST',
body: formData,
headers: {