Merge remote-tracking branch 'origin/main' into storages-to-queries

This commit is contained in:
Igor Lobanov 2022-11-30 18:06:23 +01:00
commit 084d50ac57
5 changed files with 123 additions and 79 deletions

View File

@ -5,7 +5,7 @@
{{ $upstream_port := index $port_map_list 2 }} {{ $upstream_port := index $port_map_list 2 }}
map $http_origin $allow_origin { map $http_origin $allow_origin {
~^https?:\/\/((.*\.)?localhost(:\d+)?|discoursio-webapp-git(.*)?\.vercel\.app|(.*\.)?discours\.io(:\d+)?)$ $http_origin; ~^https?:\/\/((.*\.)?localhost(:\d+)?|discoursio-webapp(-(.*))?\.vercel\.app|(.*\.)?discours\.io)$ $http_origin;
default ""; default "";
} }
@ -114,7 +114,7 @@ server {
{{ if $.PROXY_X_FORWARDED_SSL }}proxy_set_header X-Forwarded-Ssl {{ $.PROXY_X_FORWARDED_SSL }};{{ end }} {{ if $.PROXY_X_FORWARDED_SSL }}proxy_set_header X-Forwarded-Ssl {{ $.PROXY_X_FORWARDED_SSL }};{{ end }}
if ($request_method = 'OPTIONS') { if ($request_method = 'OPTIONS') {
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'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
# #
# 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
@ -131,7 +131,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,Authorization' 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;
@ -139,7 +139,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,Authorization' 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;

View File

@ -1,6 +1,6 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import Boolean, Column, String, ForeignKey, DateTime from sqlalchemy import Column, ForeignKey, DateTime, String
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from base.orm import Base from base.orm import Base
from orm.user import User from orm.user import User
@ -11,16 +11,24 @@ class CollabAuthor(Base):
id = None # type: ignore id = None # type: ignore
collab = Column(ForeignKey("collab.id"), primary_key=True) collab = Column(ForeignKey("collab.id"), primary_key=True)
authorId = Column(ForeignKey("user.id"), primary_key=True) author = Column(ForeignKey("user.id"), primary_key=True)
accepted = Column(Boolean, default=False) invitedBy = Column(ForeignKey("user.id"))
class CollabInvited(Base):
__tablename__ = "collab_invited"
id = None # type: ignore
collab = Column(ForeignKey("collab.id"), primary_key=True)
author = Column(ForeignKey("user.id"), primary_key=True)
invitedBy = Column(ForeignKey("user.id"))
class Collab(Base): class Collab(Base):
__tablename__ = "collab" __tablename__ = "collab"
title = Column(String, nullable=True, comment="Title") shout = Column(ForeignKey("shout.id"), primary_key=True)
body = Column(String, nullable=True, comment="Body")
pic = Column(String, nullable=True, comment="Picture")
authors = relationship(lambda: User, secondary=CollabAuthor.__tablename__) authors = relationship(lambda: User, secondary=CollabAuthor.__tablename__)
invites = relationship(lambda: User, secondary=CollabInvited.__tablename__)
createdAt = Column(DateTime, default=datetime.now, comment="Created At") createdAt = Column(DateTime, default=datetime.now, comment="Created At")
createdBy = Column(ForeignKey("user.id"), comment="Created By") chat = Column(String, unique=True, nullable=False)

View File

@ -1,9 +1,8 @@
from datetime import datetime, timezone
from auth.authenticate import login_required from auth.authenticate import login_required
from base.orm import local_session from base.orm import local_session
from base.resolvers import query, mutation from base.resolvers import query, mutation
from orm.collab import Collab from base.exceptions import OperationNotAllowed, ObjectNotExist
from orm.collab import Collab, CollabAuthor
from orm.shout import Shout from orm.shout import Shout
from orm.user import User from orm.user import User
@ -11,65 +10,74 @@ from orm.user import User
@query.field("getCollabs") @query.field("getCollabs")
@login_required @login_required
async def get_collabs(_, info): async def get_collabs(_, info):
auth = info.context["request"].auth user = info.context["request"].user
user_id = auth.user_id
with local_session() as session: with local_session() as session:
user = session.query(User).where(User.id == user_id).first()
collabs = session.query(Collab).filter(user.slug in Collab.authors) collabs = session.query(Collab).filter(user.slug in Collab.authors)
return collabs return collabs
@mutation.field("inviteAuthor") @mutation.field("inviteCoauthor")
@login_required @login_required
async def invite_author(_, info, author, shout): async def invite_coauthor(_, info, author: str, shout: int):
auth = info.context["request"].auth user = info.context["request"].user
user_id = auth.user_id
with local_session() as session: with local_session() as session:
shout = session.query(Shout).filter(Shout.slug == shout).first() s = session.query(Shout).where(Shout.id == shout).one()
if not shout: if not s:
return {"error": "invalid shout slug"} raise ObjectNotExist("invalid shout id")
authors = [a.id for a in shout.authors] else:
if user_id not in authors: c = session.query(Collab).where(Collab.shout == shout).one()
return {"error": "access denied"} if user.slug not in c.authors:
author = session.query(User).filter(User.id == author.id).first() raise OperationNotAllowed("you are not in authors list")
if author: else:
if author.id in authors: invited_user = session.query(User).where(User.slug == author).one()
return {"error": "already added"} c.invites.append(invited_user)
shout.authors.append(author) session.add(c)
shout.updated_at = datetime.now(tz=timezone.utc)
session.add(shout)
session.commit() session.commit()
# TODO: email notify # TODO: email notify
return {} return {}
@mutation.field("removeAuthor") @mutation.field("removeCoauthor")
@login_required @login_required
async def remove_author(_, info, author, shout): async def remove_coauthor(_, info, author: str, shout: int):
auth = info.context["request"].auth user = info.context["request"].user
user_id = auth.user_id
with local_session() as session: with local_session() as session:
shout = session.query(Shout).filter(Shout.slug == shout).first() s = session.query(Shout).where(Shout.id == shout).one()
if not shout: if not s:
return {"error": "invalid shout slug"} raise ObjectNotExist("invalid shout id")
authors = [author.id for author in shout.authors] if user.slug != s.createdBy.slug:
if user_id not in authors: raise OperationNotAllowed("only onwer can remove coauthors")
return {"error": "access denied"} else:
author = session.query(User).filter(User.slug == author).first() c = session.query(Collab).where(Collab.shout == shout).one()
if author: ca = session.query(CollabAuthor).where(c.shout == shout, c.author == author).one()
if author.id not in authors: session.remve(ca)
return {"error": "not in authors"} c.invites = filter(lambda x: x.slug == author, c.invites)
shout.authors.remove(author) c.authors = filter(lambda x: x.slug == author, c.authors)
shout.updated_at = datetime.now(tz=timezone.utc) session.add(c)
session.add(shout)
session.commit() session.commit()
# result = Result("INVITED")
# FIXME: await ShoutStorage.put(result)
# TODO: email notify # TODO: email notify
return {} return {}
@mutation.field("acceptCoauthor")
@login_required
async def accept_coauthor(_, info, shout: int):
user = info.context["request"].user
with local_session() as session:
s = session.query(Shout).where(Shout.id == shout).one()
if not s:
raise ObjectNotExist("invalid shout id")
else:
c = session.query(Collab).where(Collab.shout == shout).one()
accepted = filter(lambda x: x.slug == user.slug, c.invites).pop()
if accepted:
c.authors.append(accepted)
s.authors.append(accepted)
session.add(s)
session.add(c)
session.commit()
return {}
else:
raise OperationNotAllowed("only invited can accept")

View File

@ -7,10 +7,14 @@ from base.orm import local_session
from base.resolvers import mutation from base.resolvers import mutation
from orm.rbac import Resource from orm.rbac import Resource
from orm.shout import Shout, ShoutAuthor, ShoutTopic from orm.shout import Shout, ShoutAuthor, ShoutTopic
from orm.collab import Collab
from services.inbox import MessagesStorage
from orm.topic import TopicFollower
from orm.topic import TopicFollower, Topic from orm.topic import TopicFollower, Topic
from orm.user import User from orm.user import User
from resolvers.zine.reactions import reactions_follow, reactions_unfollow from resolvers.zine.reactions import reactions_follow, reactions_unfollow
from services.zine.gittask import GitTask from services.zine.gittask import GitTask
from resolvers.inbox.chats import create_chat
@mutation.field("createShout") @mutation.field("createShout")
@ -21,9 +25,31 @@ async def create_shout(_, info, inp):
topic_slugs = inp.get("topic_slugs", []) topic_slugs = inp.get("topic_slugs", [])
if topic_slugs: if topic_slugs:
del inp["topic_slugs"] del inp["topic_slugs"]
body = inp.get("body")
with local_session() as session: with local_session() as session:
new_shout = Shout.create(**inp) if body:
# now we should create a draft shout (can be viewed only by authors)
authors = inp.get("authors", [])
new_shout = Shout.create({
"title": inp.get("title", body[:12] + '...'),
"body": body,
"authors": authors,
"topics": inp.get("topics", []),
"mainTopic": inp.get("topics", []).pop(),
"visibility": "authors"
})
authors.remove(user.slug)
if authors:
chat = create_chat(None, info, new_shout.title, members=authors)
# create a cooperative chatroom
MessagesStorage.register_chat(chat)
# now we should create a collab
new_collab = Collab.create({
"shout": new_shout.id,
"authors": [user.slug, ],
"invites": authors
})
session.add(new_collab)
# NOTE: shout made by one first author # NOTE: shout made by one first author
sa = ShoutAuthor.create(shoutId=new_shout.id, userId=user.id) sa = ShoutAuthor.create(shoutId=new_shout.id, userId=user.id)

View File

@ -94,12 +94,13 @@ type ReactionUpdating {
################################### Inputs ################################### ################################### Inputs ###################################
input ShoutInput { input ShoutInput {
slug: String! slug: String
body: String!
community: String!
mainTopic: String
topic_slugs: [String]
title: String title: String
body: String!
authors: [String]
topics: [String]
community: Int
mainTopic: String
subtitle: String subtitle: String
versionOf: String versionOf: String
visibleForRoles: [String] # role ids are strings visibleForRoles: [String] # role ids are strings
@ -164,8 +165,8 @@ type Mutation {
confirmEmail(token: String!): AuthResult! confirmEmail(token: String!): AuthResult!
# shout # shout
createShout(input: ShoutInput!): Result! createShout(inp: ShoutInput!): Result!
updateShout(input: ShoutInput!): Result! updateShout(inp: ShoutInput!): Result!
deleteShout(slug: String!): Result! deleteShout(slug: String!): Result!
# user profile # user profile
@ -185,8 +186,9 @@ type Mutation {
deleteReaction(id: Int!): Result! deleteReaction(id: Int!): Result!
# collab # collab
inviteAuthor(author: String!, shout: String!): Result! inviteCoauthor(author: String!, shout: int!): Result!
removeAuthor(author: String!, shout: String!): Result! removeCouthor(author: String!, shout: Int!): Result!
acceptCoauthor(shout: Int!): Result!
# following # following
follow(what: FollowingEntity!, slug: String!): Result! follow(what: FollowingEntity!, slug: String!): Result!
@ -371,14 +373,6 @@ type User {
oid: String oid: String
} }
type Collab {
authors: [String]!
invites: [String]
createdAt: DateTime!
title: String
body: String
}
enum ReactionKind { enum ReactionKind {
LIKE LIKE
DISLIKE DISLIKE
@ -525,3 +519,11 @@ type Chat {
unread: Int unread: Int
private: Boolean private: Boolean
} }
type Collab {
authors: [String]!
invites: [String]
shout: Shout
chat: Chat
createdAt: Int!
}