logs-fixes-debug
Some checks failed
deploy / deploy (push) Failing after 5s

This commit is contained in:
Untone 2024-01-23 23:13:49 +03:00
parent 80fed4049a
commit 9b03e625f4
7 changed files with 141 additions and 120 deletions

View File

@ -1,6 +1,7 @@
import json import json
import time import time
import uuid import uuid
import logging
from models.chat import Chat, ChatUpdate from models.chat import Chat, ChatUpdate
from services.auth import login_required from services.auth import login_required
@ -8,6 +9,8 @@ from services.presence import notify_chat
from services.rediscache import redis from services.rediscache import redis
from services.schema import mutation from services.schema import mutation
logger = logging.getLogger("[resolvers.chats] ")
logger.setLevel(logging.DEBUG)
@mutation.field("update_chat") @mutation.field("update_chat")
@login_required @login_required
@ -25,7 +28,7 @@ async def update_chat(_, info, chat_new: ChatUpdate):
chat_str = await redis.execute("GET", f"chats/{chat_id}") chat_str = await redis.execute("GET", f"chats/{chat_id}")
if not chat_str: if not chat_str:
return {"error": "chat not exist"} return {"error": "chat not exist"}
else: elif isinstance(chat_str, str):
chat: Chat = json.loads(chat_str) chat: Chat = json.loads(chat_str)
if author_id in chat["admins"]: if author_id in chat["admins"]:
chat.update( chat.update(
@ -50,7 +53,7 @@ async def update_chat(_, info, chat_new: ChatUpdate):
async def create_chat(_, info, title="", members=None): async def create_chat(_, info, title="", members=None):
members = members or [] members = members or []
author_id = info.context["author_id"] author_id = info.context["author_id"]
chat = None chat: Chat
if author_id: if author_id:
if author_id not in members: if author_id not in members:
members.append(int(author_id)) members.append(int(author_id))
@ -58,15 +61,19 @@ async def create_chat(_, info, title="", members=None):
# NOTE: private chats has no title # NOTE: private chats has no title
# reuse private chat created before if exists # reuse private chat created before if exists
if len(members) == 2 and title == "": if len(members) == 2 and title == "":
chatset1 = set((await redis.execute("SMEMBERS", f"chats_by_author/{members[0]}")) or []) chatdata1 = await redis.execute("SMEMBERS", f"chats_by_author/{members[0]}")
chatset2 = set((await redis.execute("SMEMBERS", f"chats_by_author/{members[1]}")) or []) chatdata2 = await redis.execute("SMEMBERS", f"chats_by_author/{members[1]}")
for c in chatset1.intersection(chatset2): if isinstance(chatdata1, list) and isinstance(chatdata2, list):
chat_data = await redis.execute("GET", f"chats/{c}") chatset1 = set(chatdata1)
if chat_data: chatset2 = set(chatdata2)
chat = json.loads(chat_data)
if chat["title"] == "": for c in chatset1.intersection(chatset2):
print("[inbox] createChat found old chat") chat_data = await redis.execute("GET", f"chats/{c}")
return {"chat": chat, "error": "existed"} if isinstance(chat_data, str):
chat = json.loads(chat_data)
if chat["title"] == "":
logger.info("[inbox] createChat found old chat")
return {"chat": chat, "error": "existed"}
chat_id = str(uuid.uuid4()) chat_id = str(uuid.uuid4())
chat: Chat = { chat: Chat = {
@ -89,7 +96,8 @@ async def create_chat(_, info, title="", members=None):
await redis.execute("SET", f"chats/{chat_id}", json.dumps(chat)) await redis.execute("SET", f"chats/{chat_id}", json.dumps(chat))
await redis.execute("SET", f"chats/{chat_id}/next_message_id", str(0)) await redis.execute("SET", f"chats/{chat_id}/next_message_id", str(0))
return {"error": None, "chat": chat} return {"error": None, "chat": chat}
return {"error": "no chat was created"}
@mutation.field("delete_chat") @mutation.field("delete_chat")
@ -97,12 +105,11 @@ async def create_chat(_, info, title="", members=None):
async def delete_chat(_, info, chat_id: str): async def delete_chat(_, info, chat_id: str):
author_id = info.context["author_id"] author_id = info.context["author_id"]
chat_str = await redis.execute("GET", f"chats/{chat_id}") chat_str = await redis.execute("GET", f"chats/{chat_id}")
if chat_str: if isinstance(chat_str, str):
chat: Chat = json.loads(chat_str) chat: Chat = json.loads(chat_str)
if author_id in chat["admins"]: if author_id in chat["admins"]:
await redis.execute("DEL", f"chats/{chat_id}") await redis.execute("DEL", f"chats/{chat_id}")
await redis.execute("SREM", f"chats_by_author/{author_id}", chat_id) await redis.execute("SREM", f"chats_by_author/{author_id}", chat_id)
for member_id in chat["members"]: for member_id in chat["members"]:
await notify_chat(chat, member_id, "delete") await notify_chat(chat, member_id, "delete")
else: return {"error": "chat not exist"}
return {"error": "chat not exist"}

View File

@ -27,8 +27,9 @@ async def load_messages(
try: try:
message_ids = [] + (ids or []) message_ids = [] + (ids or [])
if limit: if limit:
mids = (await redis.lrange(f"chats/{chat_id}/message_ids", offset, offset + limit)) or [] mids = await redis.execute("LRANGE", f"chats/{chat_id}/message_ids", offset, offset + limit)
message_ids += mids if isinstance(mids, list):
message_ids.extend(mids)
if message_ids: if message_ids:
message_keys = [f"chats/{chat_id}/messages/{mid}" for mid in message_ids] message_keys = [f"chats/{chat_id}/messages/{mid}" for mid in message_ids]
messages = (await redis.mget(*message_keys)) or [] messages = (await redis.mget(*message_keys)) or []

View File

@ -21,59 +21,61 @@ async def create_message(_, info, chat_id: str, body: str, reply_to=None):
# Если данных чата нет, возвращаем ошибку # Если данных чата нет, возвращаем ошибку
if not chat_data: if not chat_data:
return {"error": "chat is not exist"} return {"error": "chat is not exist"}
else: elif isinstance(chat_data, str):
# Преобразование данных чата из строки JSON в словарь # Преобразование данных чата из строки JSON в словарь
chat_dict = json.loads(chat_data) chat_dict = json.loads(chat_data)
print(chat_dict) chat_id = chat_dict["id"]
# Получение ID следующего сообщения # Получение ID следующего сообщения
message_id = await redis.execute("GET", f"chats/{chat_dict['id']}/next_message_id") message_id = await redis.execute("GET", f"chats/{chat_dict['id']}/next_message_id")
message_id = int(message_id) if message_id else 0 if isinstance(message_id, str) or isinstance(message_id, int):
chat_id = chat_dict["id"] message_id = int(message_id) if message_id else 0
# Создание нового сообщения # Создание нового сообщения
new_message: Message = { new_message: Message = {
"chat_id": chat_id, "chat_id": chat_id,
"id": message_id, "id": message_id,
"created_by": author_id, "created_by": author_id,
"body": body, "body": body,
"created_at": int(time.time()), "created_at": int(time.time()),
"updated_at": None, "updated_at": None,
} "reply_to": None
}
# Если есть ответ, добавляем его в сообщение # Если есть ответ, добавляем его в сообщение
if reply_to: if reply_to:
new_message["reply_to"] = reply_to new_message["reply_to"] = reply_to
# Обновление времени последнего обновления чата # Обновление времени последнего обновления чата
chat_dict["updated_at"] = new_message["created_at"] chat_dict["updated_at"] = new_message["created_at"]
# Запись обновленных данных чата обратно в Redis # Запись обновленных данных чата обратно в Redis
await redis.execute("SET", f"chats/{chat_id}", json.dumps(chat_dict)) await redis.execute("SET", f"chats/{chat_id}", json.dumps(chat_dict))
print(f"[inbox] creating message {new_message}") print(f"[inbox] creating message {new_message}")
# Запись нового сообщения в Redis # Запись нового сообщения в Redis
await redis.execute( await redis.execute(
"SET", "SET",
f"chats/{chat_id}/messages/{message_id}", f"chats/{chat_id}/messages/{message_id}",
json.dumps(new_message), json.dumps(new_message),
) )
# Добавление ID нового сообщения в список ID сообщений чата # Добавление ID нового сообщения в список ID сообщений чата
await redis.execute("LPUSH", f"chats/{chat_id}/message_ids", str(message_id)) await redis.execute("LPUSH", f"chats/{chat_id}/message_ids", str(message_id))
# Обновление ID следующего сообщения # Обновление ID следующего сообщения
await redis.execute("SET", f"chats/{chat_id}/next_message_id", str(message_id + 1)) await redis.execute("SET", f"chats/{chat_id}/next_message_id", str(message_id + 1))
# Добавление нового сообщения в список непрочитанных сообщений для каждого участника чата # Добавление нового сообщения в список непрочитанных сообщений для каждого участника чата
members = chat_dict["members"] members = chat_dict["members"]
for member_id in members: for member_id in members:
await redis.execute("LPUSH", f"chats/{chat_dict['id']}/unread/{member_id}", str(message_id)) await redis.execute("LPUSH", f"chats/{chat_dict['id']}/unread/{member_id}", str(message_id))
# Отправка уведомления о новом сообщении # Отправка уведомления о новом сообщении
new_message["chat_id"] = chat_id new_message["chat_id"] = chat_id
await notify_message(new_message, "create") await notify_message(new_message, "create")
return {"message": new_message, "error": None} return {"message": new_message, "error": None}
return {"error": "cannot create message"}
@mutation.field("update_message") @mutation.field("update_message")
@ -94,24 +96,24 @@ async def update_message(_, info, message):
message = await redis.execute("GET", f"chats/{chat_id}/messages/{message_id}") message = await redis.execute("GET", f"chats/{chat_id}/messages/{message_id}")
if not message: if not message:
return {"error": "message not exist"} return {"error": "message not exist"}
elif isinstance(message, str):
message = json.loads(message)
if message["created_by"] != author_id:
return {"error": "access denied"}
message = json.loads(message) if body:
if message["created_by"] != author_id: message["body"] = body
return {"error": "access denied"} message["updated_at"] = int(time.time())
if body: await redis.execute("SET", f"chats/{chat_id}/messages/{message_id}", json.dumps(message))
message["body"] = body
message["updated_at"] = int(time.time())
await redis.execute("SET", f"chats/{chat_id}/messages/{message_id}", json.dumps(message)) # Отправка уведомления
message["chat_id"] = chat_id
await notify_message(message, "update")
# Отправка уведомления return {"message": message, "error": None}
message["chat_id"] = chat_id
await notify_message(message, "update")
return {"message": message, "error": None} return {"message": message, "error": "cannot update"}
else:
return {"message": message, "error": "cannot update, no message_id"}
@mutation.field("delete_message") @mutation.field("delete_message")
@ -122,24 +124,26 @@ async def delete_message(_, info, chat_id: str, message_id: int):
chat_str = await redis.execute("GET", f"chats/{chat_id}") chat_str = await redis.execute("GET", f"chats/{chat_id}")
if not chat_str: if not chat_str:
return {"error": "chat not exist"} return {"error": "chat not exist"}
chat = json.loads(chat_str) elif isinstance(chat_str, str):
chat = json.loads(chat_str)
message_data = await redis.execute("GET", f"chats/{chat_id}/messages/{str(message_id)}") message_data = await redis.execute("GET", f"chats/{chat_id}/messages/{str(message_id)}")
if not message_data: if not message_data:
return {"error": "message not exist"} return {"error": "message not exist"}
message: Message = json.loads(message_data) elif isinstance(message_data, str):
if message["author"] != author_id: message: Message = json.loads(message_data)
return {"error": "access denied"} if message["created_by"] != author_id:
return {"error": "access denied"}
await redis.execute("LREM", f"chats/{chat_id}/message_ids", 0, str(message_id)) await redis.execute("LREM", f"chats/{chat_id}/message_ids", 0, str(message_id))
await redis.execute("DEL", f"chats/{chat_id}/messages/{str(message_id)}") await redis.execute("DEL", f"chats/{chat_id}/messages/{str(message_id)}")
members = chat["members"] members = chat["members"]
for member_id in members: for member_id in members:
await redis.execute("LREM", f"chats/{chat_id}/unread/{member_id}", 0, str(message_id)) await redis.execute("LREM", f"chats/{chat_id}/unread/{member_id}", 0, str(message_id))
message["chat_id"] = chat_id message["chat_id"] = chat_id
await notify_message(message, "delete") await notify_message(message, "delete")
return {} return {}
@ -152,19 +156,20 @@ async def mark_as_read(_, info, chat_id: str, message_id: int):
chat_str = await redis.execute("GET", f"chats/{chat_id}") chat_str = await redis.execute("GET", f"chats/{chat_id}")
if not chat_str: if not chat_str:
return {"error": "chat not exist"} return {"error": "chat not exist"}
if isinstance(chat_str, str):
chat = json.loads(chat_str)
members = set(chat["members"])
if author_id not in members:
return {"error": "access denied"}
chat = json.loads(chat_str) await redis.execute("LREM", f"chats/{chat_id}/unread/{author_id}", 0, str(message_id))
members = set(chat["members"])
if author_id not in members:
return {"error": "access denied"}
await redis.execute("LREM", f"chats/{chat_id}/unread/{author_id}", 0, str(message_id)) message_data = await redis.execute("GET", f"chats/{chat_id}/messages/{str(message_id)}")
if not message_data:
return {"error": "message not exist"}
elif isinstance(message_data, str):
message: Message = json.loads(message_data)
message_data = await redis.execute("GET", f"chats/{chat_id}/messages/{str(message_id)}") await notify_message(message, "seen")
if not message_data:
return {"error": "message not exist"}
message: Message = json.loads(message_data)
await notify_message(message, "seen")
return {"error": None} return {"error": None}

View File

@ -19,11 +19,11 @@ async def search_recipients(_, info, text: str, limit: int = 50, offset: int = 0
author_id = info.context["author_id"] author_id = info.context["author_id"]
existed_chats = await redis.execute("SMEMBERS", f"/chats_by_author/{author_id}") existed_chats = await redis.execute("SMEMBERS", f"/chats_by_author/{author_id}")
if isinstance(existed_chats, str): if isinstance(existed_chats, set):
chats_list = list(json.loads(existed_chats)) chats_list = list(existed_chats)
for chat_id in chats_list[offset : (offset + limit)]: for chat_id in chats_list[offset: (offset + limit)]:
members_ids = await redis.execute("GET", f"/chats/{chat_id}/members") members_ids = await redis.execute("SMEMBERS", f"/chats/{chat_id}/members")
if isinstance(members_ids, list): if isinstance(members_ids, set):
for member_id in members_ids: for member_id in members_ids:
author = CacheStorage.authors_by_id.get(member_id) author = CacheStorage.authors_by_id.get(member_id)
if author: if author:
@ -36,6 +36,7 @@ async def search_recipients(_, info, text: str, limit: int = 50, offset: int = 0
return {"members": list(result), "error": None} return {"members": list(result), "error": None}
@query.field("search_messages") @query.field("search_messages")
@login_required @login_required
async def search_messages( async def search_messages(

View File

@ -5,6 +5,10 @@ from starlette.exceptions import HTTPException
from services.core import get_author_by_user from services.core import get_author_by_user
from settings import AUTH_URL from settings import AUTH_URL
import logging
logger = logging.getLogger("[services.auth] ")
logger.setLevel(logging.DEBUG)
async def check_auth(req) -> str | None: async def check_auth(req) -> str | None:
token = req.headers.get("Authorization") token = req.headers.get("Authorization")
@ -38,14 +42,14 @@ async def check_auth(req) -> str | None:
data = await response.json() data = await response.json()
errors = data.get("errors") errors = data.get("errors")
if errors: if errors:
print(f"[services.auth] errors: {errors}") logger.error(f"{errors}")
else: else:
user_id = data.get("data", {}).get(query_name, {}).get("claims", {}).get("sub") user_id = data.get("data", {}).get(query_name, {}).get("claims", {}).get("sub")
print(f"[services.auth] got user_id: {user_id}") logger.info(f"[services.auth] got user_id: {user_id}")
return user_id return user_id
except Exception as e: except Exception as e:
# Handling and logging exceptions during authentication check # Handling and logging exceptions during authentication check
print(f"[services.auth] {e}") logger.error(e)
if not user_id: if not user_id:
raise HTTPException(status_code=401, detail="Unauthorized") raise HTTPException(status_code=401, detail="Unauthorized")

View File

@ -6,21 +6,30 @@ from datetime import datetime, timezone, timedelta
from models.member import ChatMember from models.member import ChatMember
from settings import API_BASE from settings import API_BASE
import time
import logging
logger = logging.getLogger("[services.core] ")
logger.setLevel(logging.DEBUG)
def _request_endpoint(query_name, body) -> dict: def _request_endpoint(query_name, body) -> dict:
print(f"[services.core] requesting {query_name}...") ts1 = time.time()
logger.debug(f"requesting {query_name}...")
response = requests.post(API_BASE, headers={"Content-Type": "application/json"}, json=body) response = requests.post(API_BASE, headers={"Content-Type": "application/json"}, json=body)
print(f"[services.core] {query_name} response: <{response.status_code}> {response.text[:32]}..") ts2 = time.time()
logger.debug(f"{query_name} response in {ts1-ts2} secs: <{response.status_code}> {response.text[:32]}..")
if response.status_code == 200: if response.status_code == 200:
try: try:
r = response.json() r = response.json()
result = r.get("data", {}).get(query_name, {}) result = r.get("data", {}).get(query_name, {})
if result: if result:
print(f"[services.core] entries amount in result: {len(result)} ") logger.info(f"entries amount in result: {len(result)} ")
return result return result
except ValueError as e: except ValueError as e:
print(f"[services.core] Error decoding JSON response: {e}") logger.error(f"Error decoding JSON response: {e}")
return {} return {}
@ -42,7 +51,7 @@ def get_author_by_user(user: str):
gql = { gql = {
"query": f"query {operation}($user: String!) {{ {query_name}(user: $user){{ id }} }}", "query": f"query {operation}($user: String!) {{ {query_name}(user: $user){{ id }} }}",
"operationName": operation, "operationName": operation,
"variables": {"user": user}, "variables": {"user": user.strip()},
} }
return _request_endpoint(query_name, gql) return _request_endpoint(query_name, gql)
@ -74,14 +83,14 @@ class CacheStorage:
self = CacheStorage self = CacheStorage
async with self.lock: async with self.lock:
task = asyncio.create_task(self.worker()) task = asyncio.create_task(self.worker())
print(task) logger.info(task)
@staticmethod @staticmethod
async def update_authors(): async def update_authors():
self = CacheStorage self = CacheStorage
async with self.lock: async with self.lock:
result = get_all_authors() result = get_all_authors()
print(f"[services.core] loaded {len(result)}") logger.info(f"cache loaded {len(result)}")
if result: if result:
CacheStorage.authors = result CacheStorage.authors = result
for a in result: for a in result:
@ -95,20 +104,20 @@ class CacheStorage:
self = CacheStorage self = CacheStorage
while True: while True:
try: try:
print("[services.core] - updating profiles data...") logger.info(" - updating profiles data...")
await self.update_authors() await self.update_authors()
failed = 0 failed = 0
except Exception as er: except Exception as er:
failed += 1 failed += 1
print(f"[services.core] {er} - update failed #{failed}, wait 10 seconds") logger.error(f"{er} - update failed #{failed}, wait 10 seconds")
if failed > 3: if failed > 3:
print("[services.core] - not trying to update anymore") logger.error(" - not trying to update anymore")
break break
if failed == 0: if failed == 0:
when = datetime.now(timezone.utc) + timedelta(seconds=self.period) when = datetime.now(timezone.utc) + timedelta(seconds=self.period)
t = format(when.astimezone().isoformat()) t = format(when.astimezone().isoformat())
print("[services.core] ⎩ next update: %s" % (t.split("T")[0] + " " + t.split("T")[1].split(".")[0])) logger.info(" ⎩ next update: %s" % (t.split("T")[0] + " " + t.split("T")[1].split(".")[0]))
await asyncio.sleep(self.period) await asyncio.sleep(self.period)
else: else:
await asyncio.sleep(10) await asyncio.sleep(10)
print("[services.core] - trying to update data again") logger.info(" - trying to update data again")

View File

@ -24,7 +24,6 @@ class RedisCache:
return r return r
except Exception as e: except Exception as e:
print(f"[redis] error: {e}") print(f"[redis] error: {e}")
return None
async def subscribe(self, *channels): async def subscribe(self, *channels):
if self._client: if self._client:
@ -46,11 +45,6 @@ class RedisCache:
return return
await self._client.publish(channel, data) await self._client.publish(channel, data)
async def lrange(self, key, start, stop):
if self._client:
print(f"[redis] LRANGE {key} {start} {stop}")
return await self._client.lrange(key, start, stop)
async def mget(self, key, *keys): async def mget(self, key, *keys):
if self._client: if self._client:
print(f"[redis] MGET {key} {keys}") print(f"[redis] MGET {key} {keys}")