refactored

This commit is contained in:
tonyrewin 2022-08-11 08:53:14 +03:00
parent 158cb20717
commit 8aec6c6e07
46 changed files with 116 additions and 418 deletions

View File

@ -1,15 +0,0 @@
FROM python:3.8
EXPOSE 8081
RUN /usr/local/bin/python -m pip install --upgrade pip
WORKDIR /usr/src/app
COPY requirements.txt ./
RUN set -ex && pip install -r requirements.txt
COPY . .
CMD ["python", "server.py", "inbox"]

View File

@ -1,21 +1,17 @@
from functools import wraps
from typing import Optional, Tuple
from datetime import datetime, timedelta
from graphql import GraphQLResolveInfo
from jwt import DecodeError, ExpiredSignatureError
from starlette.authentication import AuthenticationBackend
from starlette.requests import HTTPConnection
from auth.credentials import AuthCredentials, AuthUser
from auth.jwtcodec import JWTCodec
from auth.authorize import Authorize, TokenStorage
from exceptions import InvalidToken, OperationNotAllowed
from base.exceptions import InvalidToken
from orm.user import User
from storages.users import UserStorage
from orm.base import local_session
from redis import redis
from base.orm import local_session
from settings import JWT_AUTH_HEADER, EMAIL_TOKEN_LIFE_SPAN

View File

@ -1,7 +1,7 @@
from datetime import datetime, timedelta
from auth.jwtcodec import JWTCodec
from redis import redis
from base.redis import redis
from settings import JWT_LIFE_SPAN
from auth.validations import User

View File

@ -1,5 +1,4 @@
from typing import List, Optional, Text
from pydantic import BaseModel

View File

@ -1,9 +1,7 @@
import requests
from starlette.responses import RedirectResponse
from starlette.exceptions import HTTPException
from auth.authenticate import EmailAuthenticate, ResetPassword
from base.orm import local_session
from settings import BACKEND_URL, MAILGUN_API_KEY, MAILGUN_DOMAIN, RESET_PWD_URL, \
CONFIRM_EMAIL_URL, ERROR_URL_ON_FRONTEND

View File

@ -1,7 +1,7 @@
from auth.password import Password
from exceptions import InvalidPassword, ObjectNotExist
from base.exceptions import InvalidPassword, ObjectNotExist
from orm import User as OrmUser
from orm.base import local_session
from base.orm import local_session
from auth.validations import User
from sqlalchemy import or_

View File

@ -1,8 +1,5 @@
from authlib.integrations.starlette_client import OAuth
from starlette.responses import RedirectResponse
from urllib.parse import quote_plus
from auth.authorize import Authorize
from auth.identity import Identity

View File

@ -1,7 +1,4 @@
from typing import Optional
import aioredis
from settings import REDIS_URL
@ -53,3 +50,9 @@ if __name__ == '__main__':
import asyncio
asyncio.run(test())
redis = Redis()
__all__ = ['redis']

View File

@ -1,34 +0,0 @@
from importlib import import_module
from ariadne import load_schema_from_path, make_executable_schema
from ariadne.asgi import GraphQL
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.middleware.sessions import SessionMiddleware
from starlette.routing import Route
from auth.authenticate import JWTAuthenticate
from redis import redis
import asyncio
from resolvers_base import resolvers
import inbox_resolvers.inbox
schema = make_executable_schema(load_schema_from_path("inbox_schema.graphql"), resolvers)
middleware = [
Middleware(AuthenticationMiddleware, backend=JWTAuthenticate()),
Middleware(SessionMiddleware, secret_key="!secret")
]
async def start_up():
await redis.connect()
async def shutdown():
await redis.disconnect()
app = Starlette(debug=True, on_startup=[start_up], on_shutdown=[shutdown], middleware=middleware)
app.mount("/", GraphQL(schema, debug=True))

View File

@ -1,87 +0,0 @@
scalar DateTime
################################### Payload
type Result {
error: String
}
type MessageResult {
error: String
message: Message
}
enum MessageStatus {
NEW
UPDATED
DELETED
}
type ChatUpdatedResult {
error: String
status: MessageStatus
message: Message
}
type CreateChatResult {
chatId: String
error: String
}
type EnterChatResult {
chat: Chat
messages: [Message]
error: String
}
type UserChatsResult {
error: String
chats: [String]
}
################################### Mutation
type Mutation {
# message
createChat(description: String): CreateChatResult!
createMessage(chatId: String!, body: String!, replyTo: Int): MessageResult!
updateMessage(chatId: String!, id: Int!, body: String!): MessageResult!
deleteMessage(chatId: String!, id: Int!): Result!
markAsRead(chatId: String!, ids: [Int]!): Result!
}
################################### Query
type Query {
userChats: UserChatsResult!
enterChat(chatId: String!, size: Int = 50): EnterChatResult!
getMessages(chatId: String!, size: Int!, page: Int!): [Message]!
}
############################################ Subscription
type Subscription {
chatUpdated(chatId: String!): ChatUpdatedResult!
}
############################################ Entities
type Message {
author: String!
chatRoom: Int!
body: String!
createdAt: DateTime!
id: Int!
replyTo: Int
updatedAt: DateTime!
visibleForUsers: [Int]!
}
type Chat {
id: Int!
createdAt: DateTime!
updatedAt: DateTime!
description: String
}

View File

@ -9,8 +9,8 @@ from starlette.routing import Route
from auth.authenticate import JWTAuthenticate
from auth.oauth import oauth_login, oauth_authorize
from auth.email import email_authorize
from redis import redis
from resolvers.base import resolvers
from base.redis import redis
from base.resolvers import resolvers
from resolvers.zine import ShoutsCache
from storages.viewed import ViewedStorage
# from storages.gittask import GitTask

View File

@ -1,180 +0,0 @@
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
error_log error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
map $http_origin $allow_origin {
~^https?://(.*\.)?localhost:3000|new.discours.io|discours.io()(:\d+)?$ $http_origin;
default "";
}
upstream discoursio-api-8080 {
server 0.0.0.0:8080;
}
server {
listen localhost:8000;
#server_name localhost;
#charset koi8-r;
# access_log logs/host.access.log main;
#location / {
# root html;
# index index.html index.htm;
#}
location / {
gzip on;
gzip_min_length 1100;
gzip_buffers 4 32k;
gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml font/truetype application/x-font-ttf font/opentype application/vnd.ms-fontobject image/svg+xml;
gzip_vary on;
gzip_comp_level 6;
proxy_pass http://discoursio-api-8080;
http2_push_preload on;
proxy_http_version 1.1;
proxy_read_timeout 60s;
proxy_buffer_size 4096;
proxy_buffering on;
proxy_buffers 8 4096;
proxy_busy_buffers_size 8192;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Request-Start $msec;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
#
# 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';
add_header 'Access-Control-Allow-Credentials' 'true';
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*' 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' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*:' 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' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
}
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
include servers/*;
}

View File

@ -9,7 +9,7 @@ from orm.reaction import Reaction
from storages.topics import TopicStorage
from storages.users import UserStorage
from storages.viewed import ViewedStorage
from orm.base import Base, engine, local_session
from base.orm import Base, engine, local_session
__all__ = ["User", "Role", "Operation", "Permission", \
"Community", "Shout", "Topic", "TopicFollower", \

View File

@ -1,6 +1,6 @@
from datetime import datetime
from sqlalchemy import Column, String, ForeignKey, DateTime
from orm.base import Base, local_session
from base.orm import Base, local_session
class CommunityFollower(Base):
__tablename__ = 'community_followers'

View File

@ -1,5 +1,5 @@
from sqlalchemy import Column, String, JSON as JSONType
from orm.base import Base
from base.orm import Base
class Notification(Base):
__tablename__ = 'notification'

View File

@ -1,7 +1,7 @@
import warnings
from sqlalchemy import String, Column, ForeignKey, UniqueConstraint, TypeDecorator
from sqlalchemy.orm import relationship
from orm.base import Base, REGISTRY, engine, local_session
from base.orm import Base, REGISTRY, engine, local_session
from orm.community import Community

View File

@ -1,6 +1,6 @@
from datetime import datetime
from sqlalchemy import Column, String, ForeignKey, DateTime
from orm.base import Base, local_session
from base.orm import Base, local_session
import enum
from sqlalchemy import Enum
from storages.viewed import ViewedStorage

View File

@ -6,7 +6,7 @@ from orm.topic import Topic, ShoutTopic
from orm.reaction import Reaction
from storages.reactions import ReactionsStorage
from storages.viewed import ViewedStorage
from orm.base import Base
from base.orm import Base
class ShoutReactionsFollower(Base):

View File

@ -1,6 +1,6 @@
from datetime import datetime
from sqlalchemy import Column, String, ForeignKey, DateTime, JSON as JSONType
from orm.base import Base
from base.orm import Base
class ShoutTopic(Base):
__tablename__ = 'shout_topic'

View File

@ -1,7 +1,7 @@
from datetime import datetime
from sqlalchemy import Column, Integer, String, ForeignKey, Boolean, DateTime, JSON as JSONType
from sqlalchemy.orm import relationship
from orm.base import Base, local_session
from base.orm import Base, local_session
from orm.rbac import Role
from storages.roles import RoleStorage

View File

@ -1,5 +0,0 @@
from redis.client import Redis
redis = Redis()
__all__ = ['redis']

View File

@ -11,6 +11,9 @@ from resolvers.editor import create_shout, delete_shout, update_shout
from resolvers.community import create_community, delete_community, get_community, get_communities
__all__ = [
"follow",
"unfollow",
# auth
"login",
"register",

View File

@ -7,10 +7,10 @@ from auth.identity import Identity
from auth.password import Password
from auth.email import send_confirm_email, send_auth_email, send_reset_password_email
from orm import User, Role
from orm.base import local_session
from resolvers.base import mutation, query
from base.orm import local_session
from base.resolvers import mutation, query
from resolvers.profile import get_user_info
from exceptions import InvalidPassword, InvalidToken
from base.exceptions import InvalidPassword, InvalidToken
from settings import JWT_AUTH_HEADER
@mutation.field("confirmEmail")

View File

@ -1,8 +1,8 @@
from datetime import datetime
from orm.base import local_session
from base.orm import local_session
from orm.shout import Shout
from orm.user import User
from resolvers.base import mutation
from base.resolvers import mutation
from auth.authenticate import login_required
@mutation.field("inviteAuthor")

View File

@ -1,7 +1,7 @@
from orm.community import Community, CommunityFollower
from orm.base import local_session
from base.orm import local_session
from orm.user import User
from resolvers.base import mutation, query
from base.resolvers import mutation, query
from auth.authenticate import login_required
from datetime import datetime
from typing import List

View File

@ -1,9 +1,9 @@
from orm import Shout
from orm.base import local_session
from base.orm import local_session
from orm.rbac import Resource
from orm.shout import ShoutAuthor, ShoutTopic
from orm.user import User
from resolvers.base import mutation
from base.resolvers import mutation
from resolvers.reactions import reactions_follow, reactions_unfollow
from auth.authenticate import login_required
from datetime import datetime

View File

@ -1,5 +1,5 @@
from auth.authenticate import login_required
from orm.base import local_session
from base.orm import local_session
from sqlalchemy import and_, desc, query
from orm.reaction import Reaction
from orm.shout import Shout, ShoutAuthor, ShoutTopic

View File

@ -1,11 +1,8 @@
from resolvers_base import mutation, query, subscription
from base.resolvers import mutation, query, subscription
from auth.authenticate import login_required
import asyncio, uuid, json
from datetime import datetime
from redis import redis
from base.redis import redis
class ChatFollowing:
queue = asyncio.Queue()

View File

@ -2,13 +2,13 @@ from orm.user import User, UserRole, Role, UserRating, AuthorFollower
from storages.users import UserStorage
from orm.shout import Shout
from orm.reaction import Reaction
from orm.base import local_session
from base.orm import local_session
from orm.topic import Topic, TopicFollower
from resolvers.base import mutation, query
from base.resolvers import mutation, query
from resolvers.community import get_followed_communities
from resolvers.reactions import get_shout_reactions
from auth.authenticate import login_required
from inbox_resolvers.inbox import get_inbox_counter
from resolvers.inbox import get_inbox_counter
from sqlalchemy import and_, desc
from sqlalchemy.orm import selectinload
from typing import List

View File

@ -1,16 +1,12 @@
from sqlalchemy import and_, desc, func, select
from sqlalchemy.orm import selectinload, joinedload
from orm.reaction import Reaction
from orm.base import local_session
from orm.shout import Shout, ShoutReactionsFollower
from base.orm import local_session
from orm.shout import ShoutReactionsFollower
from orm.user import User
from resolvers.base import mutation, query
from base.resolvers import mutation, query
from auth.authenticate import login_required
from datetime import datetime
from storages.reactions import ReactionsStorage
from storages.shoutscache import ShoutsCache
from storages.viewed import ViewedStorage
from typing import List
def reactions_follow(user, slug, auto=False):
with local_session() as session:

View File

@ -3,8 +3,8 @@ from storages.topics import TopicStorage
from orm.shout import Shout
from orm.user import User
from storages.topicstat import TopicStat
from orm.base import local_session
from resolvers.base import mutation, query
from base.orm import local_session
from base.resolvers import mutation, query
from auth.authenticate import login_required
from sqlalchemy import and_

View File

@ -1,7 +1,7 @@
from orm.shout import Shout, ShoutAuthor, ShoutTopic
from orm.topic import Topic
from orm.base import local_session
from resolvers.base import mutation, query
from base.orm import local_session
from base.resolvers import mutation, query
from storages.shoutscache import ShoutsCache
from storages.viewed import ViewedStorage
from resolvers.profile import author_follow, author_unfollow

View File

@ -1,15 +0,0 @@
from ariadne import MutationType, QueryType, SubscriptionType, ScalarType
query = QueryType()
mutation = MutationType()
subscription = SubscriptionType()
datetime_scalar = ScalarType("DateTime")
@datetime_scalar.serializer
def serialize_datetime(value):
return value.isoformat()
resolvers = [query, mutation, subscription, datetime_scalar]

View File

@ -2,19 +2,43 @@ scalar DateTime
################################### Payload ###################################
type CurrentUserInfo {
inbox: Int
topics: [String]!
authors: [String]!
reactions: [String]!
communities: [String]!
type MessageResult {
error: String
message: Message
}
enum MessageStatus {
NEW
UPDATED
DELETED
}
type ChatUpdatedResult {
error: String
status: MessageStatus
message: Message
}
type CreateChatResult {
chatId: String
error: String
}
type EnterChatResult {
chat: Chat
messages: [Message]
error: String
}
type UserChatsResult {
error: String
chats: [String]
}
type AuthResult {
error: String
token: String
user: User
info: CurrentUserInfo
}
type Result {
@ -101,6 +125,13 @@ enum FollowingEntity {
################################### Mutation
type Mutation {
# inbox
createChat(description: String): CreateChatResult!
createMessage(chatId: String!, body: String!, replyTo: Int): MessageResult!
updateMessage(chatId: String!, id: Int!, body: String!): MessageResult!
deleteMessage(chatId: String!, id: Int!): Result!
markAsRead(chatId: String!, ids: [Int]!): Result!
# auth
confirmEmail(token: String!): AuthResult!
registerUser(email: String!, password: String): AuthResult!
@ -155,6 +186,10 @@ type Mutation {
################################### Query
type Query {
# inbox
userChats: UserChatsResult!
enterChat(chatId: String!, size: Int = 50): EnterChatResult!
getMessages(chatId: String!, size: Int!, page: Int!): [Message]!
# auth
isEmailUsed(email: String!): Boolean!
@ -212,6 +247,7 @@ type Query {
############################################ Subscription
type Subscription {
chatUpdated(chatId: String!): ChatUpdatedResult!
onlineUpdated: [User!]!
shoutUpdated: Shout!
userUpdated: User!
@ -395,3 +431,21 @@ type Token {
usedAt: DateTime
value: String!
}
type Message {
author: String!
chatRoom: Int!
body: String!
createdAt: DateTime!
id: Int!
replyTo: Int
updatedAt: DateTime!
visibleForUsers: [Int]!
}
type Chat {
id: Int!
createdAt: DateTime!
updatedAt: DateTime!
description: String
}

View File

@ -1,5 +1,5 @@
import uvicorn
from settings import PORT, INBOX_SERVICE_PORT
from settings import PORT
import sys
@ -16,8 +16,5 @@ if __name__ == '__main__':
("Access-Control-Allow-Credentials", "true")
]
uvicorn.run("main:app", host="localhost", port=8080, headers=headers) #, ssl_keyfile="discours.key", ssl_certfile="discours.crt", reload=True)
elif inbox_service:
print("INBOX SERVICE")
uvicorn.run("inbox_main:app", host="0.0.0.0", port=INBOX_SERVICE_PORT)
else :
uvicorn.run("main:app", host="0.0.0.0", port=PORT)

View File

@ -1,9 +1,9 @@
import asyncio
from sqlalchemy import and_, desc, func
from sqlalchemy.orm import selectinload, joinedload
from orm.base import local_session
from sqlalchemy.orm import joinedload
from base.orm import local_session
from orm.reaction import Reaction, ReactionKind
from orm.topic import ShoutTopic, Topic
from orm.topic import ShoutTopic
def kind_to_rate(kind) -> int:

View File

@ -1,7 +1,6 @@
import asyncio
from sqlalchemy.orm import selectinload
from orm.rbac import Role
class RoleStorage:

View File

@ -1,6 +1,6 @@
import asyncio
from orm.base import local_session
from base.orm import local_session
from orm.shout import ShoutAuthor

View File

@ -3,7 +3,7 @@ import asyncio
from datetime import datetime, timedelta
from sqlalchemy import and_, desc, func, select
from sqlalchemy.orm import selectinload
from orm.base import local_session
from base.orm import local_session
from orm.reaction import Reaction
from orm.shout import Shout
from storages.reactions import ReactionsStorage

View File

@ -1,4 +1,3 @@
import asyncio
from orm.topic import Topic

View File

@ -1,7 +1,5 @@
import asyncio
from orm.base import local_session
from base.orm import local_session
from storages.shoutauthor import ShoutAuthorStorage
from orm.topic import ShoutTopic, TopicFollower
from typing import Dict

View File

@ -1,4 +1,3 @@
import asyncio
from sqlalchemy.orm import selectinload
from orm.user import User

View File

@ -1,9 +1,8 @@
import asyncio
from datetime import datetime
from sqlalchemy import Column, DateTime, ForeignKey, Integer
from sqlalchemy.orm.attributes import flag_modified
from orm.base import Base, local_session
from base.orm import Base, local_session
class ViewedByDay(Base):