formatted

This commit is contained in:
Untone 2023-10-14 15:59:43 +03:00
parent 4ddc424f29
commit 154633c114
15 changed files with 63 additions and 48 deletions

View File

@ -4,5 +4,5 @@ root = true
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace=true
trim_trailing_whitespace = true
insert_final_newline = true

View File

@ -1,11 +1,14 @@
## `inbox`: Сервер для внутренних переписок
### ENV
- REDIS_URL
- AUTH_URL
- API_BASE
- REDIS_URL
- AUTH_URL
- API_BASE
### Как это работает
__Redis__:
- Для каждого пользователя создаётся запись в хранилищах `chats_by_author/<chat_id>` и `chats/<chat_id>` и канал redis `chat:<chat_id>`, в котором публикуюутся обновления всех переписок.
- Для каждого пользователя создаётся запись в хранилищах `chats_by_author/<chat_id>` и `chats/<chat_id>` и канал
redis `chat:<chat_id>`, в котором публикуюутся обновления всех переписок.

View File

@ -1,9 +1,11 @@
import os
from importlib import import_module
from os.path import exists
from ariadne import load_schema_from_path, make_executable_schema
from ariadne.asgi import GraphQL
from starlette.applications import Starlette
from services.redis import redis
from services.schema import resolvers
from settings import DEV_SERVER_PID_FILE_NAME, SENTRY_DSN, MODE

View File

@ -11,7 +11,7 @@ authors = ["Tony Rewin <anton.rewin@gmail.com>"]
[tool.poetry.dependencies]
python = "^3.12"
sentry-sdk = "^1.32.0"
redis = {extras = ["hiredis"], version = "^5.0.1"}
redis = { extras = ["hiredis"], version = "^5.0.1" }
ariadne = "^0.20.1"
starlette = "^0.31.1"
uvicorn = "^0.23.0"
@ -20,6 +20,7 @@ itsdangerous = "^2.1.2"
[tool.poetry.dev-dependencies]
pytest = "^7.4.2"
black = { version = "^23.9.1", python = ">=3.12" }
[tool.black]
line-length = 120

View File

@ -1,14 +1,13 @@
from resolvers.chats import create_chat, delete_chat, update_chat
from resolvers.load import load_chats, load_messages_by, load_recipients
from resolvers.messages import (
create_message,
delete_message,
update_message,
mark_as_read,
)
from resolvers.load import load_chats, load_messages_by, load_recipients
from resolvers.search import search_recipients
__all__ = [
# inbox
"load_chats",

View File

@ -1,10 +1,11 @@
import json
import uuid
from datetime import datetime, timezone
from validators.inbox import Chat
from services.auth import login_required
from services.redis import redis
from services.schema import mutation
from validators.inbox import Chat
@mutation.field("updateChat")
@ -107,4 +108,3 @@ async def delete_chat(_, info, chat_id: str):
await redis.execute("SREM", f"chats_by_author/{author_id}", chat_id)
else:
return {"error": "chat not exist"}

View File

@ -1,13 +1,15 @@
import asyncio
import json
from typing import Any, Dict, List, Optional, Union
from services.auth import login_required
from services.core import get_author, get_network
from services.redis import redis
from services.auth import login_required
from services.schema import query
from validators.inbox import Message, Chat, ChatMember
from .chats import create_chat
from .unread import get_unread_counter
import asyncio
# NOTE: not an API handler
async def load_messages(
@ -21,10 +23,10 @@ async def load_messages(
message_ids = [] + ids
if limit:
mids = (
await redis.lrange(
f"chats/{chat_id}/message_ids", offset, offset + limit
)
) or []
await redis.lrange(
f"chats/{chat_id}/message_ids", offset, offset + limit
)
) or []
mids = [mid for mid in mids]
message_ids += mids
if message_ids:
@ -54,7 +56,7 @@ async def load_chats(
author_id = info.context["author_id"]
cids = (await redis.execute("SMEMBERS", f"chats_by_author/{author_id}")) or []
members_online = (await redis.execute("SMEMBERS", "authors-online")) or []
cids = list(cids)[offset : (offset + limit)]
cids = list(cids)[offset: (offset + limit)]
chats = []
lock = asyncio.Lock()
if len(cids) == 0:
@ -87,8 +89,8 @@ async def load_messages_by(_, info, by, limit: int = 10, offset: int = 0):
"""load :limit messages of :chat_id with :offset"""
author_id = info.context["author_id"]
user_chats = (
await redis.execute("SMEMBERS", "chats_by_author/" + str(author_id))
) or []
await redis.execute("SMEMBERS", "chats_by_author/" + str(author_id))
) or []
user_chats = [c for c in user_chats]
if user_chats:
messages = []

View File

@ -1,11 +1,12 @@
import json
from datetime import datetime, timezone
from typing import List
from validators.inbox import Message
from services.auth import login_required
from services.presence import notify_message
from services.redis import redis
from services.schema import mutation
from validators.inbox import Message
@mutation.field("createMessage")
@ -26,16 +27,15 @@ async def create_message(_, info, chat: str, body: str, reply_to=None):
else:
chat_dict = json.loads(chat_data)
print(chat_dict)
message_id = (
await redis.execute("GET", f"chats/{chat_dict['id']}/next_message_id")
) or 0
message_id = int(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
new_message: Message = {
"chat": chat_dict["id"],
"id": message_id,
"author": author_id,
"body": body,
"createdAt": int(datetime.now(tz=timezone.utc).timestamp()),
"updatedAt": None
}
if reply_to:
new_message["replyTo"] = reply_to
@ -110,10 +110,10 @@ async def delete_message(_, info, chat_id: str, message_id: int):
return {"error": "chat not exist"}
chat = json.loads(chat)
message = await redis.execute("GET", f"chats/{chat_id}/messages/{str(message_id)}")
if not message:
message_data = await redis.execute("GET", f"chats/{chat_id}/messages/{str(message_id)}")
if not message_data:
return {"error": "message not exist"}
message: Message = json.loads(message)
message: Message = json.loads(message_data)
if message["author"] != author_id:
return {"error": "access denied"}

View File

@ -1,10 +1,11 @@
import json
from typing import Dict, Union, List, Any
from datetime import datetime, timezone, timedelta
from typing import Dict, Union, List, Any
from resolvers.load import load_messages
from services.auth import login_required
from services.core import get_network
from services.redis import redis
from resolvers.load import load_messages
from services.schema import query
@ -18,7 +19,7 @@ async def search_recipients(_, info, text: str, limit: int = 50, offset: int = 0
author_id = info.context["author_id"]
talk_before = await redis.execute("GET", f"/chats_by_author/{author_id}")
if talk_before:
talk_before = list(json.loads(talk_before))[offset : (offset + limit)]
talk_before = list(json.loads(talk_before))[offset: (offset + limit)]
for chat_id in talk_before:
members = await redis.execute("GET", f"/chats/{chat_id}/members")
if members:
@ -68,8 +69,8 @@ async def search_in_chats(
mmm = list(
filter(
lambda msg: int(datetime.now(tz=timezone.utc))
- int(msg["createdAt"])
< timedelta(days=days_ago),
- int(msg["createdAt"])
< timedelta(days=days_ago),
mmm,
)
)

View File

@ -1,7 +1,7 @@
from services.redis import redis
import json
from services.auth import login_required
from services.redis import redis
async def get_unread_counter(chat_id: str, author_id: int) -> int:

View File

@ -1,4 +1,5 @@
import sys
import uvicorn
from uvicorn.main import logger

View File

@ -1,8 +1,9 @@
import json
from functools import wraps
from httpx import AsyncClient, HTTPError
from settings import AUTH_URL
from httpx import AsyncClient, HTTPError
from settings import AUTH_URL
INTERNAL_AUTH_SERVER = "v2.discours" in AUTH_URL or "testapi.discours" in AUTH_URL
@ -19,12 +20,12 @@ async def check_auth(req):
gql = {
"query": query_type
+ " "
+ operation
+ " { "
+ query_name
+ " { user { id } } "
+ " }",
+ " "
+ operation
+ " { "
+ query_name
+ " { user { id } } "
+ " }",
"operationName": operation,
"variables": None,
}

View File

@ -1,4 +1,5 @@
import json
from services.redis import redis
from validators.inbox import Message

View File

@ -1,4 +1,5 @@
import redis.asyncio as aredis
from settings import REDIS_URL

View File

@ -1,6 +1,7 @@
from typing import Dict, Optional, List
from typing import TypedDict, Optional, List
class Message(Dict):
class Message(TypedDict):
id: int
chat: str
author: int
@ -10,17 +11,19 @@ class Message(Dict):
createdAt: int
updatedAt: Optional[int]
class Chat(Dict):
class Chat(TypedDict):
id: str
members: List[int]
admins: List[int]
title: str
updatedAt: int
updatedAt: Optional[int]
createdAt: int
createdBy: int
description: Optional[str]
class ChatMember(Dict):
class ChatMember(TypedDict):
id: int
slug: str
name: str