This commit is contained in:
Tony Rewin 2023-10-04 20:14:06 +03:00
parent 34b7d0021d
commit 1360692b57
6 changed files with 42 additions and 38 deletions

View File

@ -4,6 +4,6 @@ WORKDIR /app
EXPOSE 8080 EXPOSE 8080
ADD nginx.conf.sigil ./ ADD nginx.conf.sigil ./
COPY requirements.txt . COPY requirements.txt .
RUN apt update && apt install -y gcc && pip install -r requirements.txt RUN apt-get update && apt-get install -y gcc && pip install -r requirements.txt
COPY . . COPY . .
CMD ["python", "server.py"] CMD ["python", "server.py"]

View File

@ -1,14 +1,14 @@
sentry-sdk sentry-sdk
aioredis aioredis~=2.0.1
ariadne ariadne~=0.20.1
starlette starlette~=0.31.1
sqlalchemy sqlalchemy~=2.0.21
graphql-core graphql-core
gql gql
uvicorn uvicorn~=0.23.2
httpx aiohttp~=3.8.5
itsdangerous itsdangerous
pydantic pydantic~=2.4.2
psycopg2-binary psycopg2-binary
######## development deps ######## development deps
isort isort

View File

@ -1,6 +1,7 @@
import json import json
from datetime import datetime, timezone from datetime import datetime, timezone
from services.auth import login_required from services.auth import login_required
from services.presence import notify_message
from services.redis import redis from services.redis import redis
from resolvers import mutation from resolvers import mutation
@ -53,11 +54,7 @@ async def create_message(_, info, chat: str, body: str, reply_to=None):
# await FollowingManager.push("chat", result) # await FollowingManager.push("chat", result)
# subscribe on updates # subscribe on updates
channel_name = ( await notify_message(new_message, chat["id"])
f"chat:{chat['id']}" if not chat["title"] else f"group:{chat['id']}"
)
new_message["kind"] = "new_message"
await redis.execute_pubsub("PUBLISH", channel_name, json.dumps(new_message))
return {"message": new_message, "error": None} return {"message": new_message, "error": None}

View File

@ -2,12 +2,6 @@ import sys
import uvicorn import uvicorn
from settings import PORT from settings import PORT
def exception_handler(exception_type, exception, traceback, debug_hook=sys.excepthook):
print(vars(traceback))
print("%s: %s" % (exception_type.__name__, exception))
log_settings = { log_settings = {
"version": 1, "version": 1,
"disable_existing_loggers": True, "disable_existing_loggers": True,
@ -53,6 +47,11 @@ local_headers = [
] ]
def exception_handler(exception_type, exception, traceback, debug_hook=sys.excepthook):
traceback.print_exc()
print("%s: %s" % (exception_type.__name__, exception))
if __name__ == "__main__": if __name__ == "__main__":
sys.excepthook = exception_handler sys.excepthook = exception_handler
uvicorn.run( uvicorn.run(

View File

@ -1,10 +1,11 @@
from typing import Optional from typing import Optional
from aiohttp.web import HTTPUnauthorized
from aiohttp.client import ClientSession
from pydantic import BaseModel from pydantic import BaseModel
from functools import wraps from functools import wraps
from starlette.authentication import AuthenticationBackend from starlette.authentication import AuthenticationBackend
from starlette.requests import HTTPConnection from starlette.requests import HTTPConnection
from graphql.error import GraphQLError
from httpx import AsyncClient
from services.db import local_session from services.db import local_session
from settings import AUTH_URL from settings import AUTH_URL
from orm.author import Author from orm.author import Author
@ -24,19 +25,13 @@ class AuthCredentials(BaseModel):
class JWTAuthenticate(AuthenticationBackend): class JWTAuthenticate(AuthenticationBackend):
async def authenticate(self, request: HTTPConnection): async def authenticate(self, request: HTTPConnection):
scopes = {} # TODO: integrate await user.get_permission
logged_in, user_id = await check_auth(request) logged_in, user_id = await check_auth(request)
return ( return (
AuthCredentials(user_id=user_id, scopes=scopes, logged_in=logged_in), AuthCredentials(user_id=user_id, logged_in=logged_in),
AuthUser(user_id=user_id, username=""), AuthUser(user_id=user_id),
) )
class Unauthorized(GraphQLError):
code = 401
message = "401 Unauthorized"
async def check_auth(req): async def check_auth(req):
token = req.headers.get("Authorization") token = req.headers.get("Authorization")
gql = ( gql = (
@ -45,14 +40,16 @@ async def check_auth(req):
else {"query": "{ session { user { id } } }"} else {"query": "{ session { user { id } } }"}
) )
headers = {"Authorization": token, "Content-Type": "application/json"} headers = {"Authorization": token, "Content-Type": "application/json"}
async with AsyncClient() as client: async with ClientSession(headers=headers) as session:
response = await client.post(AUTH_URL, headers=headers, data=gql) async with session.post(AUTH_URL, data=gql) as response:
if response.status_code != 200: if response.status != 200:
return False, None return False, None
r = response.json() r = await response.json()
user_id = r.get("data", {}).get("session", {}).get("user", {}).get("id", None) user_id = (
is_authenticated = user_id is not None r.get("data", {}).get("session", {}).get("user", {}).get("id", None)
return is_authenticated, user_id )
is_authenticated = user_id is not None
return is_authenticated, user_id
async def author_id_by_user_id(user_id): async def author_id_by_user_id(user_id):
@ -87,7 +84,7 @@ def auth_request(f):
req = args[0] req = args[0]
is_authenticated, user_id = await check_auth(req) is_authenticated, user_id = await check_auth(req)
if not is_authenticated: if not is_authenticated:
raise Unauthorized("You are not logged in") raise HTTPUnauthorized()
else: else:
author_id = await author_id_by_user_id(user_id) author_id = await author_id_by_user_id(user_id)
req["author_id"] = author_id req["author_id"] = author_id

11
services/presence.py Normal file
View File

@ -0,0 +1,11 @@
import json
from redis import redis
async def notify_message(message, chat_id: str):
channel_name = f"chat:{chat_id}"
data = {**message, "kind": "new_message"}
try:
await redis.execute_pubsub("PUBLISH", channel_name, json.dumps(data))
except Exception as e:
print(f"Failed to publish to channel {channel_name}: {e}")