Merge branch 'main' of github.com:Discours/discours-backend into viewed

This commit is contained in:
tonyrewin 2022-11-22 08:04:14 +03:00
commit 3f51515c5c
10 changed files with 31 additions and 16 deletions

View File

@ -7,7 +7,7 @@ Tech stack:
- ariadne - ariadne
- starlette - starlette
# Local development # Local development
Install deps first Install deps first
@ -40,5 +40,5 @@ python3 server.py dev
# How to do an authorized request # How to do an authorized request
Put the header 'Auth' with token from signInQuery or registerQuery. Put the header 'Authorization' with token from signInQuery or registerQuery.

View File

@ -11,6 +11,7 @@ from auth.jwtcodec import JWTCodec
from auth.tokenstorage import TokenStorage from auth.tokenstorage import TokenStorage
from base.exceptions import InvalidToken from base.exceptions import InvalidToken
from services.auth.users import UserStorage from services.auth.users import UserStorage
from settings import SESSION_TOKEN_HEADER
class SessionToken: class SessionToken:
@ -48,10 +49,12 @@ class JWTAuthenticate(AuthenticationBackend):
async def authenticate( async def authenticate(
self, request: HTTPConnection self, request: HTTPConnection
) -> Optional[Tuple[AuthCredentials, AuthUser]]: ) -> Optional[Tuple[AuthCredentials, AuthUser]]:
if "Auth" not in request.headers:
if SESSION_TOKEN_HEADER not in request.headers:
return AuthCredentials(scopes=[]), AuthUser(user_id=None) return AuthCredentials(scopes=[]), AuthUser(user_id=None)
token = request.headers.get("Auth", "") token = request.headers.get(SESSION_TOKEN_HEADER, "")
try: try:
payload = await SessionToken.verify(token) payload = await SessionToken.verify(token)
except Exception as exc: except Exception as exc:

View File

@ -25,6 +25,8 @@ async def send_auth_email(user, token, lang="ru"):
"h:X-Mailgun-Variables": "{ \"token\": \"%s\" }" % token "h:X-Mailgun-Variables": "{ \"token\": \"%s\" }" % token
} }
print('[auth.email] payload: %r' % payload) print('[auth.email] payload: %r' % payload)
# debug
# print('http://localhost:3000/?modal=auth&mode=confirm-email&token=%s' % token)
response = requests.post( response = requests.post(
api_url, api_url,
auth=("api", MAILGUN_API_KEY), auth=("api", MAILGUN_API_KEY),

View File

@ -119,7 +119,7 @@ server {
# #
# Custom headers and headers various browsers *should* be OK with but aren't # Custom headers and headers various browsers *should* be OK with but aren't
# #
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Auth'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Credentials' 'true';
# #
# Tell client that this pre-flight info is valid for 20 days # Tell client that this pre-flight info is valid for 20 days
@ -133,7 +133,7 @@ server {
if ($request_method = 'POST') { if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '$allow_origin' always; add_header 'Access-Control-Allow-Origin' '$allow_origin' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Auth' always; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Credentials' 'true' always;
} }
@ -141,7 +141,7 @@ server {
if ($request_method = 'GET') { if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '$allow_origin' always; add_header 'Access-Control-Allow-Origin' '$allow_origin' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Auth' always; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Credentials' 'true' always;
} }

View File

@ -18,6 +18,7 @@ from base.orm import local_session
from base.resolvers import mutation, query from base.resolvers import mutation, query
from orm import Role, User from orm import Role, User
from resolvers.zine.profile import user_subscriptions from resolvers.zine.profile import user_subscriptions
from settings import SESSION_TOKEN_HEADER
@mutation.field("refreshSession") @mutation.field("refreshSession")
@ -143,7 +144,6 @@ async def auth_send_link(_, _info, email, lang="ru"):
@query.field("signIn") @query.field("signIn")
async def login(_, info, email: str, password: str = "", lang: str = "ru"): async def login(_, info, email: str, password: str = "", lang: str = "ru"):
with local_session() as session: with local_session() as session:
orm_user = session.query(User).filter(User.email == email).first() orm_user = session.query(User).filter(User.email == email).first()
if orm_user is None: if orm_user is None:
@ -182,7 +182,7 @@ async def login(_, info, email: str, password: str = "", lang: str = "ru"):
@query.field("signOut") @query.field("signOut")
@login_required @login_required
async def sign_out(_, info: GraphQLResolveInfo): async def sign_out(_, info: GraphQLResolveInfo):
token = info.context["request"].headers.get("Auth", "") token = info.context["request"].headers.get(SESSION_TOKEN_HEADER, "")
status = await TokenStorage.revoke(token) status = await TokenStorage.revoke(token)
return status return status

View File

@ -10,7 +10,8 @@ from services.zine.shoutauthor import ShoutAuthorStorage
from services.stat.reacted import ReactedStorage from services.stat.reacted import ReactedStorage
def apply_filters(filters, q, user=None): def apply_filters(q, filters, user=None):
filters = {} if filters is None else filters
if filters.get("reacted") and user: if filters.get("reacted") and user:
q.join(Reaction, Reaction.createdBy == user.slug) q.join(Reaction, Reaction.createdBy == user.slug)
if filters.get("visibility"): if filters.get("visibility"):
@ -106,7 +107,7 @@ async def load_shouts_by(_, info, options):
Shout.deletedAt.is_(None) Shout.deletedAt.is_(None)
) )
user = info.context["request"].user user = info.context["request"].user
q = apply_filters(options.get("filters"), q, user) q = apply_filters(q, options.get("filters"), user)
order_by = extract_order(options.get("order_by"), q) order_by = extract_order(options.get("order_by"), q)

View File

@ -59,7 +59,7 @@ if __name__ == "__main__":
("Access-Control-Allow-Origin", "http://localhost:3000"), ("Access-Control-Allow-Origin", "http://localhost:3000"),
( (
"Access-Control-Allow-Headers", "Access-Control-Allow-Headers",
"DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Auth", "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization",
), ),
("Access-Control-Expose-Headers", "Content-Length,Content-Range"), ("Access-Control-Expose-Headers", "Content-Length,Content-Range"),
("Access-Control-Allow-Credentials", "true"), ("Access-Control-Allow-Credentials", "true"),

View File

@ -1,6 +1,7 @@
import asyncio import asyncio
from sqlalchemy.orm import selectinload from sqlalchemy.orm import selectinload
from orm.user import User from orm.user import User
from base.orm import local_session
class UserStorage: class UserStorage:
@ -20,9 +21,15 @@ class UserStorage:
@staticmethod @staticmethod
async def get_user(id): async def get_user(id):
self = UserStorage with local_session() as session:
async with self.lock: user = (
return self.users.get(id) session.query(User)
.options(selectinload(User.roles), selectinload(User.ratings))
.filter(User.id == id)
.one()
)
return user
@staticmethod @staticmethod
async def get_all_users(): async def get_all_users():

View File

@ -36,7 +36,8 @@ class ReactedStorage:
@staticmethod @staticmethod
async def get_shout_stat(slug): async def get_shout_stat(slug):
return { return {
"viewed": await ViewedStorage.get_shout(slug), # TODO
"viewed": 0, # await ViewedStorage.get_shout(slug),
"reacted": len(await ReactedStorage.get_shout(slug)), "reacted": len(await ReactedStorage.get_shout(slug)),
"commented": len(await ReactedStorage.get_comments(slug)), "commented": len(await ReactedStorage.get_comments(slug)),
"rating": await ReactedStorage.get_rating(slug), "rating": await ReactedStorage.get_rating(slug),

View File

@ -24,3 +24,4 @@ for provider in OAUTH_PROVIDERS:
} }
SHOUTS_REPO = "content" SHOUTS_REPO = "content"
SESSION_TOKEN_HEADER = "Authorization"