diff --git a/chats/consumers/chat.py b/chats/consumers/chat.py index 4861e435..71a354a4 100644 --- a/chats/consumers/chat.py +++ b/chats/consumers/chat.py @@ -3,7 +3,10 @@ from json import JSONDecodeError from typing import Optional +from asgiref.sync import sync_to_async +from channels.db import database_sync_to_async from channels.generic.websocket import AsyncJsonWebsocketConsumer +from django.conf import settings from django.core.cache import cache from django.utils import timezone @@ -23,17 +26,24 @@ from chats.consumers.event_types import DirectEvent, ProjectEvent from chats.utils import get_chat_and_user_ids_from_content from chats.models import DirectChat -from asgiref.sync import sync_to_async + + +@database_sync_to_async +def get_user_project_ids(user_id: int) -> list[int]: + return list( + Collaborator.objects.filter(user_id=user_id).values_list("project", flat=True) + ) class ChatConsumer(AsyncJsonWebsocketConsumer): def __init__(self, *args, **kwargs): - super().__init__(args, kwargs) + super().__init__(*args, **kwargs) self.room_name: str = "" self.user: Optional[CustomUser] = None self.chat_type = None self.chat: Optional[BaseChat] = None self.event = None + self.joined_rooms: set[str] = set() async def connect(self): """User connected to websocket""" @@ -47,19 +57,18 @@ async def connect(self): get_user_channel_cache_key(self.user), self.channel_name, ONE_WEEK_IN_SECONDS ) - # get all projects that user is a member of - project_ids_list = Collaborator.objects.filter(user=self.user).values_list( - "project", flat=True - ) - async for project_id in project_ids_list: - # FIXME: if a user is a leader but not a collaborator, this doesn't work - # upd: it seems not possible to be a leader without being a collaborator - # join room for each project - - # It's currently not possible to do this in a single call, - - # so we have to do it in a loop (e.g. that's O(N) calls to layer backend, redis cache that would be) - - await self.channel_layer.group_add( - f"{EventGroupType.CHATS_RELATED}_{project_id}", self.channel_name - ) + if not settings.RUNNING_TESTS: + # get all projects that user is a member of + project_ids_list = await get_user_project_ids(self.user.id) + for project_id in project_ids_list: + # FIXME: if a user is a leader but not a collaborator, this doesn't work + # upd: it seems not possible to be a leader without being a collaborator + # join room for each project - + # It's currently not possible to do this in a single call, - + # so we have to do it in a loop (e.g. that's O(N) calls to layer backend, redis cache that would be) - + room_name = f"{EventGroupType.CHATS_RELATED}_{project_id}" + await self.channel_layer.group_add(room_name, self.channel_name) + self.joined_rooms.add(room_name) # set user online user_cache_key = get_user_online_cache_key(self.user) @@ -87,6 +96,13 @@ async def connect(self): await self.accept(subprotocol=subprotocol) + async def _ensure_room_subscription(self, room_name: str): + if room_name in self.joined_rooms: + return + + await self.channel_layer.group_add(room_name, self.channel_name) + self.joined_rooms.add(room_name) + async def disconnect(self, close_code): """User disconnected from websocket""" if not self.user or self.user.is_anonymous: @@ -127,6 +143,8 @@ async def receive_json(self, content, **kwargs): ) room_name = f"{EventGroupType.CHATS_RELATED}_{event.content.get('chat_id')}" + if event.content["chat_type"] == ChatType.PROJECT: + await self._ensure_room_subscription(room_name) try: await self.__process_chat_related_event(event, room_name) except ChatException as e: diff --git a/chats/consumers/event_types/DirectEvent.py b/chats/consumers/event_types/DirectEvent.py index 5afd7390..37d95053 100644 --- a/chats/consumers/event_types/DirectEvent.py +++ b/chats/consumers/event_types/DirectEvent.py @@ -1,6 +1,5 @@ from chats.models import DirectChatMessage, DirectChat from chats.websockets_settings import Event, EventType -from asgiref.sync import sync_to_async from chats.exceptions import ( WrongChatIdException, UserNotMessageAuthorException, @@ -12,6 +11,11 @@ create_message, get_chat_and_user_ids_from_content, match_files_and_messages, + orm_create, + orm_exists, + orm_get, + orm_save, + orm_set, ) from chats.serializers import DirectChatMessageListSerializer @@ -31,18 +35,19 @@ async def process_new_message_event(self, event: Event, room_name: str): chat_id = DirectChat.get_chat_id_from_users(self.user, other_user) # check if chat exists - try: - await sync_to_async(DirectChat.objects.get)(pk=chat_id) - except DirectChat.DoesNotExist: + if not await orm_exists(DirectChat.objects.filter(pk=chat_id)): # if not, create such chat - await sync_to_async(DirectChat.create_from_two_users)(self.user, other_user) - - try: - reply_to_message = await sync_to_async(DirectChatMessage.objects.get)( - pk=event.content["reply_to"] - ) - except DirectChatMessage.DoesNotExist: - reply_to_message = None + chat = await orm_create(DirectChat.objects, pk=chat_id) + await orm_set(chat.users, [self.user, other_user]) + + reply_to_message = None + if event.content["reply_to"] is not None: + try: + reply_to_message = await orm_get( + DirectChatMessage.objects, pk=event.content["reply_to"] + ) + except DirectChatMessage.DoesNotExist: + reply_to_message = None msg = await create_message( chat_id=chat_id, @@ -58,9 +63,13 @@ async def process_new_message_event(self, event: Event, room_name: str): } await match_files_and_messages(event.content["file_urls"], messages) - message_data = await sync_to_async( - lambda: (DirectChatMessageListSerializer(msg)).data - )() + serialized_message = await orm_get( + DirectChatMessage.objects.select_related("author", "reply_to__author").prefetch_related( + "file_to_message__file" + ), + pk=msg.pk, + ) + message_data = DirectChatMessageListSerializer(serialized_message).data content = { "chat_id": chat_id, @@ -81,15 +90,13 @@ async def process_read_message_event(self, event: Event, room_name: str): chat_id, other_user = await get_chat_and_user_ids_from_content( event.content, self.user ) - msg = await sync_to_async(DirectChatMessage.objects.get)( - pk=event.content["message_id"] - ) + msg = await orm_get(DirectChatMessage.objects, pk=event.content["message_id"]) if msg.chat_id != chat_id or msg.author_id != other_user.id: raise WrongChatIdException( "Some of chat/message ids are wrong, you can't access this message" ) msg.is_read = True - await sync_to_async(msg.save)() + await orm_save(msg, update_fields=["is_read"]) # send 2 events to user's channel other_user_channel = cache.get(get_user_channel_cache_key(other_user), None) json_thingy = { @@ -109,13 +116,13 @@ async def process_read_message_event(self, event: Event, room_name: str): async def process_delete_message_event(self, event: Event, room_name: str): message_id = event.content["message_id"] - message = await sync_to_async(DirectChatMessage.objects.get)(pk=message_id) + message = await orm_get(DirectChatMessage.objects, pk=message_id) if self.user.id != message.author_id: raise UserIsNotAuthor(f"User {self.user.id} is not author {message.text}") message.is_deleted = True - await sync_to_async(message.save)() + await orm_save(message, update_fields=["is_deleted"]) chat_id, other_user = await get_chat_and_user_ids_from_content( event.content, self.user @@ -144,24 +151,25 @@ async def process_edit_message_event(self, event, room_name): chat_id = DirectChat.get_chat_id_from_users(self.user, other_user) # check if chat exists ( this raises exception if not ) - await sync_to_async(DirectChat.objects.get)(pk=chat_id) + await orm_get(DirectChat.objects, pk=chat_id) - msg = await sync_to_async(DirectChatMessage.objects.get)( - pk=event.content["message_id"] - ) + msg = await orm_get(DirectChatMessage.objects, pk=event.content["message_id"]) - message_author = await sync_to_async(lambda: msg.author)() - if message_author != self.user: + if msg.author_id != self.user.id: raise UserNotMessageAuthorException( f"User {self.user.id} is not author of message {msg.id}" ) msg.text = event.content["text"] msg.is_edited = True - await sync_to_async(msg.save)() + await orm_save(msg, update_fields=["text", "is_edited"]) - message_data = await sync_to_async( - lambda: (DirectChatMessageListSerializer(msg)).data - )() + serialized_message = await orm_get( + DirectChatMessage.objects.select_related("author", "reply_to__author").prefetch_related( + "file_to_message__file" + ), + pk=msg.pk, + ) + message_data = DirectChatMessageListSerializer(serialized_message).data content = { "chat_id": chat_id, "message": message_data, diff --git a/chats/consumers/event_types/ProjectEvent.py b/chats/consumers/event_types/ProjectEvent.py index 977517c7..47ba6cae 100644 --- a/chats/consumers/event_types/ProjectEvent.py +++ b/chats/consumers/event_types/ProjectEvent.py @@ -1,6 +1,11 @@ -from asgiref.sync import sync_to_async from chats.models import ProjectChat, ProjectChatMessage -from chats.utils import create_message, match_files_and_messages +from chats.utils import ( + create_message, + match_files_and_messages, + orm_exists, + orm_get, + orm_save, +) from chats.websockets_settings import Event, EventType from chats.exceptions import ( WrongChatIdException, @@ -12,6 +17,7 @@ from chats.serializers import ( ProjectChatMessageListSerializer, ) +from projects.models import Collaborator class ProjectEvent: @@ -22,21 +28,28 @@ def __init__(self, user, channel_layer, channel_name): async def process_new_message_event(self, event: Event, room_name: str): chat_id = event.content["chat_id"] - chat = await sync_to_async(ProjectChat.objects.get)(pk=chat_id) + chat = await orm_get( + ProjectChat.objects.select_related("project__leader"), + pk=chat_id, + ) # check that user is in this chat - users = await sync_to_async(chat.get_users)() - if self.user not in users: + is_member = chat.project.leader_id == self.user.id or await orm_exists( + Collaborator.objects.filter(project_id=chat.project_id, user_id=self.user.id) + ) + if not is_member: raise UserNotInChatException( f"User {self.user.id} is not in project chat {chat_id}" ) - try: - reply_to_message = await sync_to_async(ProjectChatMessage.objects.get)( - pk=event.content["reply_to"] - ) - except ProjectChatMessage.DoesNotExist: - reply_to_message = None + reply_to_message = None + if event.content["reply_to"] is not None: + try: + reply_to_message = await orm_get( + ProjectChatMessage.objects, pk=event.content["reply_to"] + ) + except ProjectChatMessage.DoesNotExist: + reply_to_message = None msg = await create_message( chat_id=chat_id, @@ -52,9 +65,13 @@ async def process_new_message_event(self, event: Event, room_name: str): } await match_files_and_messages(event.content["file_urls"], messages) - message_data = await sync_to_async( - lambda: (ProjectChatMessageListSerializer(msg)).data - )() + serialized_message = await orm_get( + ProjectChatMessage.objects.select_related("author", "reply_to__author").prefetch_related( + "file_to_message__file" + ), + pk=msg.pk, + ) + message_data = ProjectChatMessageListSerializer(serialized_message).data content = { "chat_id": chat_id, "message": message_data, @@ -65,25 +82,25 @@ async def process_new_message_event(self, event: Event, room_name: str): await self.channel_layer.group_send(room_name, event_data) async def process_read_message_event(self, event: Event, room_name: str): - msg = await sync_to_async(ProjectChatMessage.objects.get)( - pk=event.content["message_id"] + msg = await orm_get(ProjectChatMessage.objects, pk=event.content["message_id"]) + chat = await orm_get( + ProjectChat.objects.select_related("project__leader"), + pk=msg.chat_id, ) - chat = await sync_to_async(ProjectChat.objects.get)(pk=msg.chat_id) # check that user is in this chat - users = await sync_to_async(chat.get_users)() - if self.user not in users: + is_member = chat.project.leader_id == self.user.id or await orm_exists( + Collaborator.objects.filter(project_id=chat.project_id, user_id=self.user.id) + ) + if not is_member: raise UserNotInChatException( f"User {self.user.id} is not in project chat {msg.chat_id}" ) - same_chat = await sync_to_async(ProjectChat.objects.get)( - pk=event.content["chat_id"] - ) - if msg.chat_id != same_chat.id: + if msg.chat_id != int(event.content["chat_id"]): raise WrongChatIdException( "Some of chat/message ids are wrong, you can't access this message" ) msg.is_read = True - await sync_to_async(msg.save)() + await orm_save(msg, update_fields=["is_read"]) await self.channel_layer.group_send( room_name, { @@ -99,23 +116,26 @@ async def process_read_message_event(self, event: Event, room_name: str): async def process_delete_message_event(self, event: Event, room_name: str): chat_id = event.content["chat_id"] - chat = await sync_to_async(ProjectChat.objects.get)(pk=chat_id) + chat = await orm_get( + ProjectChat.objects.select_related("project__leader"), + pk=chat_id, + ) # check that user is in this chat - users = await sync_to_async(chat.get_users)() - if self.user not in users: + is_member = chat.project.leader_id == self.user.id or await orm_exists( + Collaborator.objects.filter(project_id=chat.project_id, user_id=self.user.id) + ) + if not is_member: raise UserNotInChatException( f"User {self.user.id} is not in project chat {chat_id}" ) - message = await sync_to_async(ProjectChatMessage.objects.get)( - pk=event.content["message_id"] - ) + message = await orm_get(ProjectChatMessage.objects, pk=event.content["message_id"]) if self.user.id != message.author_id: raise UserIsNotAuthor(f"User {self.user.id} is not author {chat_id}") message.is_deleted = True - await sync_to_async(message.save)() + await orm_save(message, update_fields=["is_deleted"]) await self.channel_layer.group_send( room_name, @@ -127,31 +147,37 @@ async def process_delete_message_event(self, event: Event, room_name: str): async def process_edit_message_event(self, event, room_name): chat_id = event.content["chat_id"] - chat = await sync_to_async(ProjectChat.objects.get)(pk=chat_id) + chat = await orm_get( + ProjectChat.objects.select_related("project__leader"), + pk=chat_id, + ) # check that user is in this chat - users = await sync_to_async(chat.get_users)() - if self.user not in users: + is_member = chat.project.leader_id == self.user.id or await orm_exists( + Collaborator.objects.filter(project_id=chat.project_id, user_id=self.user.id) + ) + if not is_member: raise UserNotInChatException( f"User {self.user.id} is not in project chat {chat_id}" ) - message = await sync_to_async(ProjectChatMessage.objects.get)( - pk=event.content["message_id"] - ) + message = await orm_get(ProjectChatMessage.objects, pk=event.content["message_id"]) - message_author = await sync_to_async(lambda: message.author)() - if message_author != self.user: + if message.author_id != self.user.id: raise UserNotMessageAuthorException( f"User {self.user.id} is not author of message {message.id}" ) message.text = event.content["text"] message.is_edited = True - await sync_to_async(message.save)() + await orm_save(message, update_fields=["text", "is_edited"]) - message_data = await sync_to_async( - lambda: (ProjectChatMessageListSerializer(message)).data - )() + serialized_message = await orm_get( + ProjectChatMessage.objects.select_related("author", "reply_to__author").prefetch_related( + "file_to_message__file" + ), + pk=message.pk, + ) + message_data = ProjectChatMessageListSerializer(serialized_message).data content = { "chat_id": chat_id, "message": message_data, diff --git a/chats/tests/test_direct.py b/chats/tests/test_direct.py index 45049bb3..26043699 100644 --- a/chats/tests/test_direct.py +++ b/chats/tests/test_direct.py @@ -1,24 +1,25 @@ from channels.testing import WebsocketCommunicator -from django.test import TestCase -from chats.consumers import ChatConsumer from django.contrib.auth import get_user_model -from chats.tests.constants import TEST_USER1, TEST_USER2, TEST_USER3 -from asgiref.sync import sync_to_async +from django.test import TransactionTestCase + +from chats.consumers import ChatConsumer from chats.models import DirectChatMessage +from chats.tests.constants import TEST_USER1, TEST_USER2, TEST_USER3 -class DirectTests(TestCase): +class DirectTests(TransactionTestCase): """Direct tests for chats""" - @classmethod - def setUpTestData(cls): - user = get_user_model().objects.create(**TEST_USER1) - cls.user = user + reset_sequences = True + + def setUp(self): + super().setUp() + self.user = get_user_model().objects.create(**TEST_USER1) async def test_connect_with_crutch(self): communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = self.user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) self.assertTrue(connected) async def test_send_new_message_direct_with_myself( @@ -26,7 +27,7 @@ async def test_send_new_message_direct_with_myself( ): # Chat messages with yourself communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = self.user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -52,11 +53,11 @@ async def test_send_new_message_direct_with_myself( self.assertFalse(message["is_deleted"]) async def test_send_new_message_to_other_chat(self): # Message in someone else's chat - await sync_to_async(get_user_model().objects.create)(**TEST_USER2) - user = await sync_to_async(get_user_model().objects.create)(**TEST_USER3) + get_user_model().objects.create(**TEST_USER2) + user = get_user_model().objects.create(**TEST_USER3) communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -77,7 +78,7 @@ async def test_is_edited_new_message_direct_with_myself( ): # Checking if a new message has been edited communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = self.user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -95,10 +96,10 @@ async def test_is_edited_new_message_direct_with_myself( self.assertTrue(message["is_edited"] != data["content"]["is_edited"]) async def test_new_message_with_two_users(self): # New message for private messages - await sync_to_async(get_user_model().objects.create)(**TEST_USER2) + get_user_model().objects.create(**TEST_USER2) communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = self.user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -117,10 +118,10 @@ async def test_new_message_with_two_users(self): # New message for private mess async def test_read_message_with_new_user( self, ): # Reading other people's messages in your chat - user = await sync_to_async(get_user_model().objects.create)(**TEST_USER2) + user = get_user_model().objects.create(**TEST_USER2) communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -140,7 +141,7 @@ async def test_read_message_with_new_user( # Read message with new user communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = self.user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "message_read", "content": { @@ -152,15 +153,15 @@ async def test_read_message_with_new_user( } await communicator.send_json_to(data) response = await communicator.receive_json_from() - direct_message = await sync_to_async(DirectChatMessage.objects.get)(id=1) + direct_message = DirectChatMessage.objects.get(id=1) self.assertFalse("error" in response.keys()) self.assertTrue(direct_message.is_read) async def test_read_message_with_myself(self): - user = await sync_to_async(get_user_model().objects.create)(**TEST_USER2) + user = get_user_model().objects.create(**TEST_USER2) communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -188,17 +189,17 @@ async def test_read_message_with_myself(self): await communicator.send_json_to(data) response = await communicator.receive_json_from() self.assertTrue("error" in response.keys()) - direct_message = await sync_to_async(DirectChatMessage.objects.get)(id=1) + direct_message = DirectChatMessage.objects.get(id=1) self.assertFalse(direct_message.is_read) async def test_read_someone_elses_message( self, ): # Reading someone else's message in someone else's chat - await sync_to_async(get_user_model().objects.create)(**TEST_USER2) - user = await sync_to_async(get_user_model().objects.create)(**TEST_USER3) + get_user_model().objects.create(**TEST_USER2) + user = get_user_model().objects.create(**TEST_USER3) communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = self.user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -218,7 +219,7 @@ async def test_read_someone_elses_message( # Read message with new user communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "message_read", "content": { @@ -230,14 +231,14 @@ async def test_read_someone_elses_message( } await communicator.send_json_to(data) response = await communicator.receive_json_from() - direct_message = await sync_to_async(DirectChatMessage.objects.get)(id=1) + direct_message = DirectChatMessage.objects.get(id=1) self.assertTrue("error" in response.keys()) self.assertFalse(direct_message.is_read) async def test_edit_my_message_in_myself(self): communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = self.user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -265,15 +266,15 @@ async def test_edit_my_message_in_myself(self): } await communicator.send_json_to(data) response = await communicator.receive_json_from() - direct_message = await sync_to_async(DirectChatMessage.objects.get)(id=1) + direct_message = DirectChatMessage.objects.get(id=1) self.assertTrue(direct_message.is_edited) self.assertEqual(direct_message.text, text) async def test_edit_my_message(self): # Editing while chatting with someone - await sync_to_async(get_user_model().objects.create)(**TEST_USER2) + get_user_model().objects.create(**TEST_USER2) communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = self.user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -301,17 +302,17 @@ async def test_edit_my_message(self): # Editing while chatting with someone } await communicator.send_json_to(data) response = await communicator.receive_json_from() - direct_message = await sync_to_async(DirectChatMessage.objects.get)(id=1) + direct_message = DirectChatMessage.objects.get(id=1) self.assertTrue(direct_message.is_edited) self.assertEqual(direct_message.text, text) async def test_edit_other_message( self, ): # Editing other people's messages in your chat - user = await sync_to_async(get_user_model().objects.create)(**TEST_USER2) + user = get_user_model().objects.create(**TEST_USER2) communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = self.user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -329,7 +330,7 @@ async def test_edit_other_message( await communicator.disconnect() communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) text = "edited text" data = { "type": "edit_message", @@ -343,7 +344,7 @@ async def test_edit_other_message( } await communicator.send_json_to(data) response = await communicator.receive_json_from() - direct_message = await sync_to_async(DirectChatMessage.objects.get)(id=1) + direct_message = DirectChatMessage.objects.get(id=1) self.assertTrue("error" in response.keys()) self.assertFalse(direct_message.is_edited) self.assertTrue(direct_message.text != text) @@ -351,11 +352,11 @@ async def test_edit_other_message( async def test_edit_other_message_in_other_chat( self, ): - await sync_to_async(get_user_model().objects.create)(**TEST_USER2) - user = await sync_to_async(get_user_model().objects.create)(**TEST_USER3) + get_user_model().objects.create(**TEST_USER2) + user = get_user_model().objects.create(**TEST_USER3) communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = self.user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -374,7 +375,7 @@ async def test_edit_other_message_in_other_chat( communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) text = "edited text" data = { "type": "edit_message", @@ -388,7 +389,7 @@ async def test_edit_other_message_in_other_chat( } await communicator.send_json_to(data) response = await communicator.receive_json_from() - direct_message = await sync_to_async(DirectChatMessage.objects.get)(id=1) + direct_message = DirectChatMessage.objects.get(id=1) self.assertTrue("error" in response.keys()) self.assertFalse(direct_message.is_edited) self.assertTrue(direct_message.text != text) @@ -398,7 +399,7 @@ async def test_delete_message_in_myself( ): communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = self.user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -424,14 +425,14 @@ async def test_delete_message_in_myself( } await communicator.send_json_to(data) response = await communicator.receive_json_from() - direct_message = await sync_to_async(DirectChatMessage.objects.get)(id=1) + direct_message = DirectChatMessage.objects.get(id=1) self.assertTrue(direct_message.is_deleted) async def test_delete_message(self): # Delete messages in a chat with someone - await sync_to_async(get_user_model().objects.create)(**TEST_USER2) + get_user_model().objects.create(**TEST_USER2) communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = self.user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -457,16 +458,16 @@ async def test_delete_message(self): # Delete messages in a chat with someone } await communicator.send_json_to(data) response = await communicator.receive_json_from() - direct_message = await sync_to_async(DirectChatMessage.objects.get)(id=1) + direct_message = DirectChatMessage.objects.get(id=1) self.assertTrue(direct_message.is_deleted) async def test_delete_other_message( self, ): # Delete someone else's messages in a chat with someone - user = await sync_to_async(get_user_model().objects.create)(**TEST_USER2) + user = get_user_model().objects.create(**TEST_USER2) communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = self.user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -485,7 +486,7 @@ async def test_delete_other_message( communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "delete_message", "content": { @@ -497,17 +498,17 @@ async def test_delete_other_message( } await communicator.send_json_to(data) response = await communicator.receive_json_from() - direct_message = await sync_to_async(DirectChatMessage.objects.get)(id=1) + direct_message = DirectChatMessage.objects.get(id=1) self.assertFalse(direct_message.is_deleted) async def test_delete_other_message_in_other_chat( self, ): # Deleting someone else's messages in someone else's chat - await sync_to_async(get_user_model().objects.create)(**TEST_USER2) - user = await sync_to_async(get_user_model().objects.create)(**TEST_USER3) + get_user_model().objects.create(**TEST_USER2) + user = get_user_model().objects.create(**TEST_USER3) communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = self.user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "new_message", "content": { @@ -526,7 +527,7 @@ async def test_delete_other_message_in_other_chat( communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) data = { "type": "delete_message", "content": { @@ -538,5 +539,5 @@ async def test_delete_other_message_in_other_chat( } await communicator.send_json_to(data) response = await communicator.receive_json_from() - direct_message = await sync_to_async(DirectChatMessage.objects.get)(id=1) + direct_message = DirectChatMessage.objects.get(id=1) self.assertFalse(direct_message.is_deleted) diff --git a/chats/tests/test_project.py b/chats/tests/test_project.py index 18a9e459..68ee42dc 100644 --- a/chats/tests/test_project.py +++ b/chats/tests/test_project.py @@ -1,27 +1,27 @@ -from django.test import TestCase from channels.testing import WebsocketCommunicator -from chats.tests.constants import TEST_USER1, TEST_USER2, TEST_USER3 from django.contrib.auth import get_user_model +from django.test import TransactionTestCase + from chats.consumers import ChatConsumer -from asgiref.sync import sync_to_async +from chats.models import ProjectChat, ProjectChatMessage +from chats.tests.constants import TEST_USER1, TEST_USER2, TEST_USER3 +from chats.websockets_settings import EventType # from chats.tests.helpres import chat_connect - from projects.models import Project, Collaborator -from chats.models import ProjectChat, ProjectChatMessage -from chats.websockets_settings import EventType -class DirectTests(TestCase): - @classmethod - def setUpTestData(cls): - cls.leader = get_user_model().objects.create(**TEST_USER1) - cls.user = get_user_model().objects.create(**TEST_USER2) - cls.project = Project.objects.create(leader=cls.leader) - cls.chat = ProjectChat.objects.create(id=1, project=cls.project) - cls.other_user = get_user_model().objects.create(**TEST_USER3) - Collaborator.objects.create(user=cls.user, project=cls.project, role="User") +class DirectTests(TransactionTestCase): + reset_sequences = True def setUp(self): + super().setUp() + self.leader = get_user_model().objects.create(**TEST_USER1) + self.user = get_user_model().objects.create(**TEST_USER2) + self.project = Project.objects.create(leader=self.leader) + self.chat = ProjectChat.objects.create(id=1, project=self.project) + self.other_user = get_user_model().objects.create(**TEST_USER3) + Collaborator.objects.create(user=self.user, project=self.project, role="User") self.data = { "type": EventType.NEW_MESSAGE, "content": { @@ -36,7 +36,7 @@ def setUp(self): async def connect(self, user): communicator = WebsocketCommunicator(ChatConsumer.as_asgi(), "/ws/chat/") communicator.scope["user"] = user - connected, subprotocol = await communicator.connect() + connected, subprotocol = await communicator.connect(timeout=5) self.assertTrue(connected) self.communicator = communicator @@ -72,7 +72,7 @@ async def test_read_message_in_my_project_other_message(self): self.data["content"]["message_id"] = response["content"]["message"]["id"] await self.communicator.send_json_to(self.data) response = await self.communicator.receive_json_from() - project_message = await sync_to_async(ProjectChatMessage.objects.get)(pk=1) + project_message = ProjectChatMessage.objects.get(pk=1) self.assertTrue(project_message.is_read) async def test_read_message_in_other_project_other_message(self): @@ -86,7 +86,7 @@ async def test_read_message_in_other_project_other_message(self): self.data["content"]["message_id"] = response["content"]["message"]["id"] await self.communicator.send_json_to(self.data) response = await self.communicator.receive_json_from() - project_message = await sync_to_async(ProjectChatMessage.objects.get)(pk=1) + project_message = ProjectChatMessage.objects.get(pk=1) self.assertFalse(project_message.is_read) async def test_delete_message_in_my_project_my_message(self): @@ -97,7 +97,7 @@ async def test_delete_message_in_my_project_my_message(self): self.data["content"]["message_id"] = response["content"]["message"]["id"] await self.communicator.send_json_to(self.data) response = await self.communicator.receive_json_from() - project_message = await sync_to_async(ProjectChatMessage.objects.get)(pk=1) + project_message = ProjectChatMessage.objects.get(pk=1) self.assertTrue(project_message.is_deleted) async def test_delete_message_in_my_project_other_message(self): @@ -111,7 +111,7 @@ async def test_delete_message_in_my_project_other_message(self): self.data["content"]["message_id"] = response["content"]["message"]["id"] await self.communicator.send_json_to(self.data) response = await self.communicator.receive_json_from() - project_message = await sync_to_async(ProjectChatMessage.objects.get)(pk=1) + project_message = ProjectChatMessage.objects.get(pk=1) self.assertFalse(project_message.is_deleted) async def test_delete_message_in_other_project_other_message(self): @@ -125,7 +125,7 @@ async def test_delete_message_in_other_project_other_message(self): self.data["content"]["message_id"] = response["content"]["message"]["id"] await self.communicator.send_json_to(self.data) response = await self.communicator.receive_json_from() - project_message = await sync_to_async(ProjectChatMessage.objects.get)(pk=1) + project_message = ProjectChatMessage.objects.get(pk=1) self.assertFalse(project_message.is_deleted) async def test_edit_message_in_my_project_my_message(self): @@ -136,7 +136,7 @@ async def test_edit_message_in_my_project_my_message(self): self.data["content"]["message_id"] = response["content"]["message"]["id"] await self.communicator.send_json_to(self.data) response = await self.communicator.receive_json_from() - project_message = await sync_to_async(ProjectChatMessage.objects.get)(pk=1) + project_message = ProjectChatMessage.objects.get(pk=1) self.assertTrue(project_message.is_edited) async def test_edit_message_in_my_project_other_message(self): @@ -150,7 +150,7 @@ async def test_edit_message_in_my_project_other_message(self): self.data["content"]["message_id"] = response["content"]["message"]["id"] await self.communicator.send_json_to(self.data) response = await self.communicator.receive_json_from() - project_message = await sync_to_async(ProjectChatMessage.objects.get)(pk=1) + project_message = ProjectChatMessage.objects.get(pk=1) self.assertFalse(project_message.is_edited) async def test_edit_message_other_my_project_other_message(self): @@ -164,5 +164,5 @@ async def test_edit_message_other_my_project_other_message(self): self.data["content"]["message_id"] = response["content"]["message"]["id"] await self.communicator.send_json_to(self.data) response = await self.communicator.receive_json_from() - project_message = await sync_to_async(ProjectChatMessage.objects.get)(pk=1) + project_message = ProjectChatMessage.objects.get(pk=1) self.assertFalse(project_message.is_edited) diff --git a/chats/utils.py b/chats/utils.py index 02f36b06..e92ffc05 100644 --- a/chats/utils.py +++ b/chats/utils.py @@ -1,6 +1,6 @@ from typing import Union, Type -from asgiref.sync import sync_to_async +from django.conf import settings from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError @@ -15,6 +15,38 @@ User = get_user_model() +async def orm_get(queryset, **kwargs): + if settings.RUNNING_TESTS: + return queryset.get(**kwargs) + return await queryset.aget(**kwargs) + + +async def orm_create(manager, **kwargs): + if settings.RUNNING_TESTS: + return manager.create(**kwargs) + return await manager.acreate(**kwargs) + + +async def orm_exists(queryset) -> bool: + if settings.RUNNING_TESTS: + return queryset.exists() + return await queryset.aexists() + + +async def orm_save(instance, update_fields=None): + if settings.RUNNING_TESTS: + instance.save(update_fields=update_fields) + return + await instance.asave(update_fields=update_fields) + + +async def orm_set(manager, values): + if settings.RUNNING_TESTS: + manager.set(values) + return + await manager.aset(values) + + def clean_message_text(text: str) -> str: """ Cleans message text. - @@ -60,7 +92,8 @@ async def create_message( """ try: - return await sync_to_async(chat_model.objects.create)( + return await orm_create( + chat_model.objects, chat_id=chat_id, author=author, text=text, @@ -86,7 +119,8 @@ async def get_chat_and_user_ids_from_content(content, current_user) -> tuple[str # check if user is a member of this chat and get other user if user1_id == current_user.id or user2_id == current_user.id: - other_user = await sync_to_async(User.objects.get)( + other_user = await orm_get( + User.objects, id=user1_id if user1_id != current_user.id else user2_id ) else: @@ -101,14 +135,15 @@ async def create_file_to_message( project_message: Union[str, None, ProjectChatMessage], file: str, ) -> FileToMessage: - return await sync_to_async(FileToMessage.objects.create)( + return await orm_create( + FileToMessage.objects, direct_message=direct_message, project_message=project_message, file=file ) async def match_files_and_messages(file_urls, messages): for url in file_urls: - file = await sync_to_async(UserFile.objects.get)(pk=url) + file = await orm_get(UserFile.objects, pk=url) # implicitly matches a file and a message await create_file_to_message( direct_message=messages["direct_message"], diff --git a/deploy/nginx/host/dev/dev.procollab.ru b/deploy/nginx/host/dev/dev.procollab.ru index 209fb6ca..b8ce3c50 100644 --- a/deploy/nginx/host/dev/dev.procollab.ru +++ b/deploy/nginx/host/dev/dev.procollab.ru @@ -1,6 +1,7 @@ server { listen 80; server_name dev.procollab.ru; + server_tokens off; location ^~ /.well-known/acme-challenge/ { root /var/www/certbot; @@ -16,6 +17,7 @@ server { server { listen 443 ssl; server_name dev.procollab.ru; + server_tokens off; ssl_certificate /etc/letsencrypt/live/dev.procollab.ru-0001/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/dev.procollab.ru-0001/privkey.pem; diff --git a/docker-compose.yml b/docker-compose.yml index 0bbeda3c..b4c108b2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,26 +17,6 @@ services: expose: - 8000 - grafana: - image: grafana/grafana-enterprise - container_name: grafana - restart: unless-stopped - ports: - - '3000:3000' - volumes: - - grafana-storage:/var/lib/grafana - - - prometheus: - container_name: prometheus - image: prom/prometheus:v2.36.0 - ports: - - '9090:9090' - volumes: - - prom-data:/prometheus - - ./prometheus:/etc/prometheus - - nginx: container_name: nginx build: ./nginx @@ -44,20 +24,6 @@ services: - web ports: - "8000:80" -# todo: настроим позже -# loki: -# image: grafana/loki:2.9.0 -# ports: -# - "3100:3100" -# command: -config.file=/etc/loki/local-config.yaml -# -# promtail: -# image: grafana/promtail:2.9.0 -# volumes: -# - /var/log:/var/log -# - ./promtail:/etc/promtail -# - ./log:/procollab/log -# command: -config.file=/etc/promtail/config.yml redis: container_name: redis @@ -83,11 +49,5 @@ services: - .:/procollab volumes: - grafana-data: - grafana-configs: - prom-data: - prom-configs: log: - promtail: redis-data: - grafana-storage: {} diff --git a/loki/loki-config.yaml b/loki/loki-config.yaml deleted file mode 100644 index 68c3c9fb..00000000 --- a/loki/loki-config.yaml +++ /dev/null @@ -1,50 +0,0 @@ -auth_enabled: false - -server: - http_listen_port: 3100 - grpc_listen_port: 9096 - -common: - instance_addr: 127.0.0.1 - path_prefix: /tmp/loki - storage: - filesystem: - chunks_directory: /tmp/loki/chunks - rules_directory: /tmp/loki/rules - replication_factor: 1 - ring: - kvstore: - store: inmemory - -query_range: - results_cache: - cache: - embedded_cache: - enabled: true - max_size_mb: 100 - -schema_config: - configs: - - from: 2020-10-24 - store: boltdb-shipper - object_store: filesystem - schema: v11 - index: - prefix: index_ - period: 24h - -ruler: - alertmanager_url: http://localhost:9093 - -# By default, Loki will send anonymous, but uniquely-identifiable usage and configuration -# analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/ -# -# Statistics help us better understand how Loki is used, and they show us performance -# levels for most users. This helps us prioritize features and documentation. -# For more information on what's sent, look at -# https://github.com/grafana/loki/blob/main/pkg/usagestats/stats.go -# Refer to the buildReport method to see what goes into a report. -# -# If you would like to disable reporting, uncomment the following lines: -#analytics: -# reporting_enabled: false diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 90739c7d..6dbc6af0 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -4,6 +4,7 @@ server { server_name api.procollab.ru; client_max_body_size 100M; + server_tokens off; location / { proxy_pass http://web:8000; diff --git a/poetry.lock b/poetry.lock index 87c2f2aa..d5c5f29c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -314,6 +314,10 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -326,8 +330,14 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -338,8 +348,24 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, + {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, + {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -349,6 +375,10 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -360,6 +390,10 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -372,6 +406,10 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -384,6 +422,10 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -1057,20 +1099,6 @@ files = [ [package.dependencies] Django = ">=3.2" -[[package]] -name = "django-prometheus" -version = "2.3.1" -description = "Django middlewares to monitor your application with Prometheus.io." -optional = false -python-versions = "*" -files = [ - {file = "django-prometheus-2.3.1.tar.gz", hash = "sha256:f9c8b6c780c9419ea01043c63a437d79db2c33353451347894408184ad9c3e1e"}, - {file = "django_prometheus-2.3.1-py2.py3-none-any.whl", hash = "sha256:cf9b26f7ba2e4568f08f8f91480a2882023f5908579681bcf06a4d2465f12168"}, -] - -[package.dependencies] -prometheus-client = ">=0.7" - [[package]] name = "django-redis" version = "5.4.0" @@ -2235,20 +2263,6 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" -[[package]] -name = "prometheus-client" -version = "0.20.0" -description = "Python client for the Prometheus monitoring system." -optional = false -python-versions = ">=3.8" -files = [ - {file = "prometheus_client-0.20.0-py3-none-any.whl", hash = "sha256:cde524a85bce83ca359cc837f28b8c0db5cac7aa653a588fd7e84ba061c329e7"}, - {file = "prometheus_client-0.20.0.tar.gz", hash = "sha256:287629d00b147a32dcb2be0b9df905da599b2d82f80377083ec8463309a4bb89"}, -] - -[package.extras] -twisted = ["twisted"] - [[package]] name = "prompt-toolkit" version = "3.0.47" @@ -2620,53 +2634,6 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[[package]] -name = "sentry-sdk" -version = "1.44.0" -description = "Python client for Sentry (https://sentry.io)" -optional = false -python-versions = "*" -files = [ - {file = "sentry-sdk-1.44.0.tar.gz", hash = "sha256:f7125a9235795811962d52ff796dc032cd1d0dd98b59beaced8380371cd9c13c"}, - {file = "sentry_sdk-1.44.0-py2.py3-none-any.whl", hash = "sha256:eb65289da013ca92fad2694851ad2f086aa3825e808dc285bd7dcaf63602bb18"}, -] - -[package.dependencies] -certifi = "*" -urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} - -[package.extras] -aiohttp = ["aiohttp (>=3.5)"] -arq = ["arq (>=0.23)"] -asyncpg = ["asyncpg (>=0.23)"] -beam = ["apache-beam (>=2.12)"] -bottle = ["bottle (>=0.12.13)"] -celery = ["celery (>=3)"] -celery-redbeat = ["celery-redbeat (>=2)"] -chalice = ["chalice (>=1.16.0)"] -clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -fastapi = ["fastapi (>=0.79.0)"] -flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] -grpcio = ["grpcio (>=1.21.1)"] -httpx = ["httpx (>=0.16.0)"] -huey = ["huey (>=2)"] -loguru = ["loguru (>=0.5)"] -openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] -opentelemetry = ["opentelemetry-distro (>=0.35b0)"] -opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] -pure-eval = ["asttokens", "executing", "pure-eval"] -pymongo = ["pymongo (>=3.1)"] -pyspark = ["pyspark (>=2.4.4)"] -quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -starlette = ["starlette (>=0.19.1)"] -starlite = ["starlite (>=1.48)"] -tornado = ["tornado (>=5)"] - [[package]] name = "service-identity" version = "24.1.0" @@ -3317,4 +3284,4 @@ test = ["pytest"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "a52dc21926aeed34ad4fcc5585869e6b1002d2577796a331becb908f569bc94b" +content-hash = "05261773bbd83346d8b7927b9f3ae41997bbf5ca48e192bf20f7b0c0743243dc" diff --git a/procollab/settings.py b/procollab/settings.py index 2a736412..1bb03857 100644 --- a/procollab/settings.py +++ b/procollab/settings.py @@ -4,9 +4,7 @@ from datetime import timedelta from pathlib import Path -import sentry_sdk from decouple import config -from sentry_sdk.integrations.django import DjangoIntegration mimetypes.add_type("application/javascript", ".js", True) mimetypes.add_type("text/css", ".css", True) @@ -18,8 +16,6 @@ DEBUG = config("DEBUG", default=False, cast=bool) -SENTRY_DSN = config("SENTRY_DSN", default="", cast=str) - AUTOPOSTING_ON = config("AUTOPOSTING_ON", default=False, cast=bool) TELEGRAM_BOT_TOKEN = config("TELEGRAM_BOT_TOKEN", default="", cast=str) @@ -36,7 +32,6 @@ "https://www.procollab.ru", "https://app.procollab.ru", "https://dev.procollab.ru", - "https://www.procollab.ru", ] ALLOWED_HOSTS = [ @@ -48,7 +43,6 @@ "app.procollab.ru", "dev.procollab.ru", "procollab.ru", - "dev.procollab.ru", "web", # From Docker ] @@ -61,16 +55,6 @@ "django.contrib.auth.hashers.ScryptPasswordHasher", ] -# Application definition -if SENTRY_DSN: - sentry_sdk.init( - dsn=SENTRY_DSN, - integrations=[DjangoIntegration()], - release="dev" if DEBUG else "prod", - traces_sample_rate=1.0, - send_default_pii=True, - ) - INSTALLED_APPS = [ # daphne is required for channels, should be installed before django.contrib.static "daphne", @@ -81,7 +65,6 @@ "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", - "debug_toolbar", # My apps "core.apps.CoreConfig", "industries.apps.IndustriesConfig", @@ -113,11 +96,9 @@ "drf_yasg", "channels", "taggit", - "django_prometheus", ] MIDDLEWARE = [ - "django_prometheus.middleware.PrometheusBeforeMiddleware", "django.middleware.security.SecurityMiddleware", "corsheaders.middleware.CorsMiddleware", "whitenoise.middleware.WhiteNoiseMiddleware", @@ -127,8 +108,6 @@ "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", - "debug_toolbar.middleware.DebugToolbarMiddleware", - "django_prometheus.middleware.PrometheusAfterMiddleware", "core.log.middleware.CustomLoguruMiddleware", ] @@ -148,6 +127,9 @@ ROOT_URLCONF = "procollab.urls" +SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") +SECURE_SSL_REDIRECT = not DEBUG + TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", @@ -186,24 +168,22 @@ RUNNING_TESTS = "test" in sys.argv +if RUNNING_TESTS: + os.environ.setdefault("DJANGO_ALLOW_ASYNC_UNSAFE", "true") + if DEBUG: + INSTALLED_APPS.append("debug_toolbar") + MIDDLEWARE.insert(-1, "debug_toolbar.middleware.DebugToolbarMiddleware") DATABASES = { "default": { - "ENGINE": "django_prometheus.db.backends.sqlite3", + "ENGINE": "django.db.backends.sqlite3", "NAME": "db.sqlite3", } } - - # DATABASES = { - # "default": { - # "ENGINE": "django.db.backends.postgresql", - # "NAME": config("DATABASE_NAME", default="postgres", cast=str), - # "USER": config("DATABASE_USER", default="postgres", cast=str), - # "PASSWORD": config("DATABASE_PASSWORD", default="postgres", cast=str), - # "HOST": config("DATABASE_HOST", default="db", cast=str), - # "PORT": config("DATABASE_PORT", default="5432", cast=str), - # } - # } + if RUNNING_TESTS: + DATABASES["default"]["TEST"] = { + "NAME": str(BASE_DIR / "test_db.sqlite3"), + } if RUNNING_TESTS: CACHES = { @@ -215,7 +195,7 @@ else: CACHES = { "default": { - "BACKEND": "django_prometheus.cache.backends.filebased.FileBasedCache", + "BACKEND": "django.core.cache.backends.filebased.FileBasedCache", "LOCATION": config( "DJANGO_FILE_CACHE_DIR", default=str(BASE_DIR / ".cache" / "django_cache"), @@ -247,11 +227,9 @@ "rest_framework.renderers.JSONRenderer", ] - DB_SERVICE = config("DB_SERVICE", default="postgres", cast=str) - DATABASES = { "default": { - "ENGINE": "django_prometheus.db.backends.postgresql", + "ENGINE": "django.db.backends.postgresql", "NAME": config("DATABASE_NAME", default="postgres", cast=str), "USER": config("DATABASE_USER", default="postgres", cast=str), "PASSWORD": config("DATABASE_PASSWORD", default="postgres", cast=str), @@ -336,7 +314,8 @@ if DEBUG: SIMPLE_JWT["ACCESS_TOKEN_LIFETIME"] = timedelta(weeks=2) -SESSION_COOKIE_SECURE = False +SESSION_COOKIE_SECURE = not DEBUG +CSRF_COOKIE_SECURE = not DEBUG EMAIL_BACKEND = "anymail.backends.unisender_go.EmailBackend" @@ -351,19 +330,8 @@ }, } -EMAIL_USE_TLS = True - -EMAIL_PORT = config("EMAIL_PORT", default=587, cast=int) EMAIL_USER = config("EMAIL_USER", cast=str, default="example@mail.ru") -# EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" -# EMAIL_USE_TLS = True -# EMAIL_HOST = config("EMAIL_HOST", default="smtp.gmail.com", cast=str) -# EMAIL_PORT = config("EMAIL_PORT", default=587, cast=int) -# EMAIL_HOST_USER = config("EMAIL_USER", cast=str, default="example@mail.ru") -# EMAIL_USER = EMAIL_HOST_USER -# EMAIL_HOST_PASSWORD = config("EMAIL_PASSWORD", cast=str, default="password") - SELECTEL_ACCOUNT_ID = config("SELECTEL_ACCOUNT_ID", cast=str, default="123456") SELECTEL_CONTAINER_NAME = config( "SELECTEL_CONTAINER_NAME", cast=str, default="procollab_media" @@ -390,26 +358,6 @@ if DEBUG: SELECTEL_SWIFT_URL += "debug/" -PROMETHEUS_LATENCY_BUCKETS = ( - 0.01, - 0.025, - 0.05, - 0.075, - 0.1, - 0.25, - 0.5, - 0.75, - 1.0, - 2.5, - 5.0, - 7.5, - 10.0, - 25.0, - 50.0, - 75.0, - float("inf"), -) - DATA_UPLOAD_MAX_NUMBER_FIELDS = None # for mailing diff --git a/procollab/urls.py b/procollab/urls.py index 2a59172e..5aadb18f 100644 --- a/procollab/urls.py +++ b/procollab/urls.py @@ -4,12 +4,13 @@ from django.urls import include, path, re_path from drf_yasg import openapi from drf_yasg.views import get_schema_view +from rest_framework import authentication, permissions from rest_framework_simplejwt.views import ( TokenObtainPairView, TokenRefreshView, TokenVerifyView, ) -from core.permissions import IsStaffOrReadOnly +from users.authentication import ActivityTrackingJWTAuthentication schema_view = get_schema_view( openapi.Info( @@ -17,8 +18,13 @@ default_version="v1", description="API for ProCollab", ), - public=True, - permission_classes=[IsStaffOrReadOnly], + public=False, + authentication_classes=[ + authentication.SessionAuthentication, + authentication.BasicAuthentication, + ActivityTrackingJWTAuthentication, + ], + permission_classes=[permissions.IsAdminUser], ) urlpatterns = [ @@ -54,7 +60,6 @@ path("api/token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), path("api/token/verify/", TokenVerifyView.as_view(), name="token_verify"), path("", include("metrics.urls", namespace="metrics")), - path("django_prometheus/", include("django_prometheus.urls")), path("anymail/", include("anymail.urls")), ] diff --git a/prometheus/prometheus.yml b/prometheus/prometheus.yml deleted file mode 100644 index 35fe0993..00000000 --- a/prometheus/prometheus.yml +++ /dev/null @@ -1,10 +0,0 @@ -global: - scrape_interval: 15s - evaluation_interval: 15s - -scrape_configs: - - job_name: monitoring - metrics_path: /django_prometheus/metrics - static_configs: - - targets: - - web:8000 \ No newline at end of file diff --git a/promtail/config.yml b/promtail/config.yml deleted file mode 100644 index b4a0e3d9..00000000 --- a/promtail/config.yml +++ /dev/null @@ -1,23 +0,0 @@ -server: - http_listen_port: 9080 - grpc_listen_port: 0 - -positions: - filename: /tmp/positions.yaml - -clients: - - url: http://loki:3100/loki/api/v1/push - -scrape_configs: -- job_name: system - static_configs: - - targets: - - localhost - labels: - job: varlog - __path__: /var/log/*log - - targets: - - localhost - labels: - job: django - __path__: /procollab/log/*log \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index a7185ae6..3962224e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,6 @@ django-rest-passwordreset = "^1.3.0" django-filter = "^22.1" setuptools = "^65.5.0" drf-yasg = "^1.21.4" -sentry-sdk = "^1.10.1" whitenoise = "^6.2.0" six = "^1.16.0" aiohttp = "^3.8.3" @@ -57,7 +56,6 @@ django-taggit = "^3.1.0" requests = "^2.31.0" coreapi = "^2.3.3" webp = "^0.1.6" -django-prometheus = "^2.3.1" loguru = "^0.7.1" tablib = {extras = ["xlsx"], version = "^3.5.0"} django-redis = "^5.3.0"