merged-isolated-core
Some checks failed
deploy / deploy (push) Failing after 1m46s

This commit is contained in:
2023-10-23 17:47:11 +03:00
parent b675188013
commit bf241a8fbd
56 changed files with 1683 additions and 2784 deletions

View File

@@ -1,34 +1,8 @@
from services.db import Base, engine
from orm.community import Community
from orm.rbac import Operation, Resource, Permission, Role
from orm.reaction import Reaction
from base.orm import Base, engine
from orm.shout import Shout
from orm.topic import Topic, TopicFollower
from orm.user import User, UserRating
def init_tables():
Base.metadata.create_all(engine)
Operation.init_table()
Resource.init_table()
User.init_table()
Community.init_table()
Role.init_table()
UserRating.init_table()
Shout.init_table()
print("[orm] tables initialized")
__all__ = [
"User",
"Role",
"Operation",
"Permission",
"Community",
"Shout",
"Topic",
"TopicFollower",
"Reaction",
"UserRating",
"init_tables"
]

67
orm/author.py Normal file
View File

@@ -0,0 +1,67 @@
from datetime import datetime
from sqlalchemy import JSON as JSONType
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from base.orm import Base, local_session
class AuthorRating(Base):
__tablename__ = "author_rating"
id = None # type: ignore
rater = Column(ForeignKey("author.id"), primary_key=True, index=True)
author = Column(ForeignKey("author.id"), primary_key=True, index=True)
value = Column(Integer)
@staticmethod
def init_table():
pass
class AuthorFollower(Base):
__tablename__ = "author_follower"
id = None # type: ignore
follower = Column(ForeignKey("author.id"), primary_key=True, index=True)
author = Column(ForeignKey("author.id"), primary_key=True, index=True)
createdAt = Column(DateTime, nullable=False, default=datetime.now)
auto = Column(Boolean, nullable=False, default=False)
class Author(Base):
__tablename__ = "author"
user = Column(Integer, nullable=False) # unbounded link with authorizer's User type
bio = Column(String, nullable=True, comment="Bio") # status description
about = Column(String, nullable=True, comment="About") # long and formatted
userpic = Column(String, nullable=True, comment="Userpic")
name = Column(String, nullable=True, comment="Display name")
slug = Column(String, unique=True, comment="Author's slug")
muted = Column(Boolean, default=False)
createdAt = Column(DateTime, nullable=False, default=datetime.now)
lastSeen = Column(DateTime, nullable=False, default=datetime.now) # Td se 0e
deletedAt = Column(DateTime, nullable=True, comment="Deleted at")
links = Column(JSONType, nullable=True, comment="Links")
ratings = relationship(AuthorRating, foreign_keys=AuthorRating.author)
@staticmethod
def init_table():
with local_session() as session:
default = session.query(Author).filter(Author.slug == "anonymous").first()
if not default:
default_dict = {
"user": 0,
"name": "Аноним",
"slug": "anonymous",
}
default = Author.create(**default_dict)
session.add(default)
discours_dict = {
"user": 1,
"name": "Дискурс",
"slug": "discours",
}
discours = Author.create(**discours_dict)
session.add(discours)
session.commit()
Author.default_author = default

View File

@@ -1,8 +1,6 @@
from datetime import datetime
from sqlalchemy import Column, DateTime, ForeignKey, String
from services.db import Base
from base.orm import Base
class ShoutCollection(Base):
@@ -21,5 +19,5 @@ class Collection(Base):
body = Column(String, nullable=True, comment="Body")
pic = Column(String, nullable=True, comment="Picture")
createdAt = Column(DateTime, default=datetime.now, comment="Created At")
createdBy = Column(ForeignKey("user.id"), comment="Created By")
createdBy = Column(ForeignKey("author.id"), comment="Created By")
publishedAt = Column(DateTime, default=datetime.now, comment="Published At")

View File

@@ -1,39 +1,45 @@
from datetime import datetime
from sqlalchemy import Column, String, ForeignKey, DateTime
from services.db import Base, local_session
from sqlalchemy.orm import relationship
from base.orm import Base, local_session
from orm.author import Author
class CommunityFollower(Base):
__tablename__ = "community_followers"
class CommunityRole:
__tablename__ = "community_role"
name = Column(String, nullable=False)
class CommunityAuthor(Base):
__tablename__ = "community_author"
id = None # type: ignore
follower = Column(ForeignKey("user.id"), primary_key=True)
follower = Column(ForeignKey("author.id"), primary_key=True)
community = Column(ForeignKey("community.id"), primary_key=True)
joinedAt = Column(
DateTime, nullable=False, default=datetime.now, comment="Created at"
)
# role = Column(ForeignKey(Role.id), nullable=False, comment="Role for member")
joinedAt = Column(DateTime, nullable=False, default=datetime.now)
role = Column(ForeignKey("community_role.id"), nullable=False)
class Community(Base):
__tablename__ = "community"
name = Column(String, nullable=False, comment="Name")
slug = Column(String, nullable=False, unique=True, comment="Slug")
name = Column(String, nullable=False)
slug = Column(String, nullable=False, unique=True)
desc = Column(String, nullable=False, default="")
pic = Column(String, nullable=False, default="")
createdAt = Column(
DateTime, nullable=False, default=datetime.now, comment="Created at"
)
createdAt = Column(DateTime, nullable=False, default=datetime.now)
authors = relationship(lambda: Author, secondary=CommunityAuthor.__tablename__, nullable=True)
@staticmethod
def init_table():
with local_session() as session:
d = session.query(Community).filter(Community.slug == "discours").first()
d = (session.query(Community).filter(Community.slug == "discours").first())
if not d:
d = Community.create(name="Дискурс", slug="discours")
session.add(d)
session.commit()
Community.default_community = d
print("[orm] default community id: %s" % d.id)
print('[orm] default community id: %s' % d.id)

View File

@@ -1,182 +0,0 @@
import warnings
from sqlalchemy import String, Column, ForeignKey, UniqueConstraint, TypeDecorator
from sqlalchemy.orm import relationship
from services.db import Base, REGISTRY, engine, local_session
# Role Based Access Control #
class ClassType(TypeDecorator):
impl = String
@property
def python_type(self):
return NotImplemented
def process_literal_param(self, value, dialect):
return NotImplemented
def process_bind_param(self, value, dialect):
return value.__name__ if isinstance(value, type) else str(value)
def process_result_value(self, value, dialect):
class_ = REGISTRY.get(value)
if class_ is None:
warnings.warn(f"Can't find class <{value}>,find it yourself!", stacklevel=2)
return class_
class Role(Base):
__tablename__ = "role"
name = Column(String, nullable=False, comment="Role Name")
desc = Column(String, nullable=True, comment="Role Description")
community = Column(
ForeignKey("community.id", ondelete="CASCADE"),
nullable=False,
comment="Community",
)
permissions = relationship(lambda: Permission)
@staticmethod
def init_table():
with local_session() as session:
r = session.query(Role).filter(Role.name == "author").first()
if r:
Role.default_role = r
return
r1 = Role.create(
name="author",
desc="Role for an author",
community=1,
)
session.add(r1)
Role.default_role = r1
r2 = Role.create(
name="reader",
desc="Role for a reader",
community=1,
)
session.add(r2)
r3 = Role.create(
name="expert",
desc="Role for an expert",
community=1,
)
session.add(r3)
r4 = Role.create(
name="editor",
desc="Role for an editor",
community=1,
)
session.add(r4)
class Operation(Base):
__tablename__ = "operation"
name = Column(String, nullable=False, unique=True, comment="Operation Name")
@staticmethod
def init_table():
with local_session() as session:
for name in ["create", "update", "delete", "load"]:
"""
* everyone can:
- load shouts
- load topics
- load reactions
- create an account to become a READER
* readers can:
- update and delete their account
- load chats
- load messages
- create reaction of some shout's author allowed kinds
- create shout to become an AUTHOR
* authors can:
- update and delete their shout
- invite other authors to edit shout and chat
- manage allowed reactions for their shout
* pros can:
- create/update/delete their community
- create/update/delete topics for their community
"""
op = session.query(Operation).filter(Operation.name == name).first()
if not op:
op = Operation.create(name=name)
session.add(op)
session.commit()
class Resource(Base):
__tablename__ = "resource"
resourceClass = Column(
String, nullable=False, unique=True, comment="Resource class"
)
name = Column(String, nullable=False, unique=True, comment="Resource name")
# TODO: community = Column(ForeignKey())
@staticmethod
def init_table():
with local_session() as session:
for res in [
"shout",
"topic",
"reaction",
"chat",
"message",
"invite",
"community",
"user",
]:
r = session.query(Resource).filter(Resource.name == res).first()
if not r:
r = Resource.create(name=res, resourceClass=res)
session.add(r)
session.commit()
class Permission(Base):
__tablename__ = "permission"
__table_args__ = (
UniqueConstraint("role", "operation", "resource"),
{"extend_existing": True},
)
role = Column(
ForeignKey("role.id", ondelete="CASCADE"), nullable=False, comment="Role"
)
operation = Column(
ForeignKey("operation.id", ondelete="CASCADE"),
nullable=False,
comment="Operation",
)
resource = Column(
ForeignKey("resource.id", ondelete="CASCADE"),
nullable=False,
comment="Resource",
)
if __name__ == "__main__":
Base.metadata.create_all(engine)
ops = [
Permission(role=1, operation=1, resource=1),
Permission(role=1, operation=2, resource=1),
Permission(role=1, operation=3, resource=1),
Permission(role=1, operation=4, resource=1),
Permission(role=2, operation=4, resource=1),
]
global_session.add_all(ops)
global_session.commit()

View File

@@ -1,9 +1,7 @@
from datetime import datetime
from enum import Enum as Enumeration
from sqlalchemy import Column, DateTime, Enum, ForeignKey, String
from services.db import Base
from base.orm import Base
class ReactionKind(Enumeration):
@@ -26,25 +24,15 @@ class ReactionKind(Enumeration):
class Reaction(Base):
__tablename__ = "reaction"
body = Column(String, nullable=True, comment="Reaction Body")
createdAt = Column(
DateTime, nullable=False, default=datetime.now, comment="Created at"
)
createdBy = Column(
ForeignKey("user.id"), nullable=False, index=True, comment="Sender"
)
createdAt = Column(DateTime, nullable=False, default=datetime.now)
createdBy = Column(ForeignKey("author.id"), nullable=False, index=True)
updatedAt = Column(DateTime, nullable=True, comment="Updated at")
updatedBy = Column(
ForeignKey("user.id"), nullable=True, index=True, comment="Last Editor"
)
updatedBy = Column(ForeignKey("author.id"), nullable=True, index=True)
deletedAt = Column(DateTime, nullable=True, comment="Deleted at")
deletedBy = Column(
ForeignKey("user.id"), nullable=True, index=True, comment="Deleted by"
)
deletedBy = Column(ForeignKey("author.id"), nullable=True, index=True)
shout = Column(ForeignKey("shout.id"), nullable=False, index=True)
replyTo = Column(
ForeignKey("reaction.id"), nullable=True, comment="Reply to reaction ID"
)
range = Column(String, nullable=True, comment="Range in format <start index>:<end>")
kind = Column(Enum(ReactionKind), nullable=False, comment="Reaction kind")
oid = Column(String, nullable=True, comment="Old ID")
replyTo = Column(ForeignKey("reaction.id"), nullable=True)
range = Column(String, nullable=True, comment="<start index>:<end>")
kind = Column(Enum(ReactionKind), nullable=False)

View File

@@ -1,12 +1,21 @@
from datetime import datetime
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, JSON
from enum import Enum as Enumeration
from sqlalchemy import (
Enum,
Boolean,
Column,
DateTime,
ForeignKey,
Integer,
String,
JSON,
)
from sqlalchemy.orm import column_property, relationship
from services.db import Base, local_session
from base.orm import Base, local_session
from orm.community import Community
from orm.reaction import Reaction
from orm.topic import Topic
from orm.user import User
from orm.author import Author
class ShoutTopic(Base):
@@ -21,7 +30,7 @@ class ShoutReactionsFollower(Base):
__tablename__ = "shout_reactions_followers"
id = None # type: ignore
follower = Column(ForeignKey("user.id"), primary_key=True, index=True)
follower = Column(ForeignKey("author.id"), primary_key=True, index=True)
shout = Column(ForeignKey("shout.id"), primary_key=True, index=True)
auto = Column(Boolean, nullable=False, default=False)
createdAt = Column(
@@ -35,48 +44,61 @@ class ShoutAuthor(Base):
id = None # type: ignore
shout = Column(ForeignKey("shout.id"), primary_key=True, index=True)
user = Column(ForeignKey("user.id"), primary_key=True, index=True)
author = Column(ForeignKey("author.id"), primary_key=True, index=True)
caption = Column(String, nullable=True, default="")
class ShoutCommunity:
__tablename__ = "shout_community"
id = None # type: ignore
shout = Column(ForeignKey("shout.id"), primary_key=True, index=True)
community = Column(ForeignKey("community.id"), primary_key=True, index=True)
class ShoutVisibility(Enumeration):
AUTHORS = 0
COMMUNITY = 1
PUBLIC = 2
class Shout(Base):
__tablename__ = "shout"
# timestamps
createdAt = Column(
DateTime, nullable=False, default=datetime.now, comment="Created at"
)
updatedAt = Column(DateTime, nullable=True, comment="Updated at")
createdAt = Column(DateTime, nullable=False, default=datetime.now)
updatedAt = Column(DateTime, nullable=True)
publishedAt = Column(DateTime, nullable=True)
deletedAt = Column(DateTime, nullable=True)
createdBy = Column(ForeignKey("user.id"), comment="Created By")
deletedBy = Column(ForeignKey("user.id"), nullable=True)
createdBy = Column(ForeignKey("author.id"), comment="Created By")
deletedBy = Column(ForeignKey("author.id"), nullable=True)
body = Column(String, nullable=False, comment="Body")
slug = Column(String, unique=True)
cover = Column(String, nullable=True, comment="Cover image url")
lead = Column(String, nullable=True)
description = Column(String, nullable=True)
body = Column(String, nullable=False, comment="Body")
title = Column(String, nullable=True)
subtitle = Column(String, nullable=True)
layout = Column(String, nullable=True)
media = Column(JSON, nullable=True)
authors = relationship(lambda: User, secondary=ShoutAuthor.__tablename__)
topics = relationship(lambda: Topic, secondary=ShoutTopic.__tablename__)
# views from the old Discours website
viewsOld = Column(Integer, default=0)
# views from Ackee tracker on the new Discours website
viewsAckee = Column(Integer, default=0)
views = column_property(viewsOld + viewsAckee)
authors = relationship(lambda: Author, secondary=ShoutAuthor.__tablename__)
topics = relationship(lambda: Topic, secondary=ShoutTopic.__tablename__)
communities = relationship(
lambda: Community, secondary=ShoutCommunity.__tablename__
)
reactions = relationship(lambda: Reaction)
viewsOld = Column(Integer, default=0)
viewsAckee = Column(Integer, default=0)
views = column_property(viewsOld + viewsAckee)
visibility = Column(Enum(ShoutVisibility), default=ShoutVisibility.AUTHORS)
# TODO: these field should be used or modified
community = Column(ForeignKey("community.id"), default=1)
lang = Column(String, nullable=False, default="ru", comment="Language")
mainTopic = Column(ForeignKey("topic.slug"), nullable=True)
visibility = Column(String, nullable=True) # owner authors community public
versionOf = Column(ForeignKey("shout.id"), nullable=True)
oid = Column(String, nullable=True)

View File

@@ -1,19 +1,15 @@
from datetime import datetime
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, String
from services.db import Base
from base.orm import Base
class TopicFollower(Base):
__tablename__ = "topic_followers"
id = None # type: ignore
follower = Column(ForeignKey("user.id"), primary_key=True, index=True)
follower = Column(ForeignKey("author.id"), primary_key=True, index=True)
topic = Column(ForeignKey("topic.id"), primary_key=True, index=True)
createdAt = Column(
DateTime, nullable=False, default=datetime.now, comment="Created at"
)
createdAt = Column(DateTime, nullable=False, default=datetime.now)
auto = Column(Boolean, nullable=False, default=False)
@@ -24,5 +20,5 @@ class Topic(Base):
title = Column(String, nullable=False, comment="Title")
body = Column(String, nullable=True, comment="Body")
pic = Column(String, nullable=True, comment="Picture")
community = Column(ForeignKey("community.id"), default=1, comment="Community")
community = Column(ForeignKey("community.id"), default=1)
oid = Column(String, nullable=True, comment="Old ID")

View File

@@ -1,106 +0,0 @@
from datetime import datetime
from sqlalchemy import JSON as JSONType
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from services.db import Base, local_session
from orm.rbac import Role
class UserRating(Base):
__tablename__ = "user_rating"
id = None # type: ignore
rater = Column(ForeignKey("user.id"), primary_key=True, index=True)
user = Column(ForeignKey("user.id"), primary_key=True, index=True)
value = Column(Integer)
@staticmethod
def init_table():
pass
class UserRole(Base):
__tablename__ = "user_role"
id = None # type: ignore
user = Column(ForeignKey("user.id"), primary_key=True, index=True)
role = Column(ForeignKey("role.id"), primary_key=True, index=True)
class AuthorFollower(Base):
__tablename__ = "author_follower"
id = None # type: ignore
follower = Column(ForeignKey("user.id"), primary_key=True, index=True)
author = Column(ForeignKey("user.id"), primary_key=True, index=True)
createdAt = Column(
DateTime, nullable=False, default=datetime.now, comment="Created at"
)
auto = Column(Boolean, nullable=False, default=False)
class User(Base):
__tablename__ = "user"
default_user = None
email = Column(String, unique=True, nullable=False, comment="Email")
username = Column(String, nullable=False, comment="Login")
password = Column(String, nullable=True, comment="Password")
bio = Column(String, nullable=True, comment="Bio") # status description
about = Column(String, nullable=True, comment="About") # long and formatted
userpic = Column(String, nullable=True, comment="Userpic")
name = Column(String, nullable=True, comment="Display name")
slug = Column(String, unique=True, comment="User's slug")
muted = Column(Boolean, default=False)
emailConfirmed = Column(Boolean, default=False)
createdAt = Column(
DateTime, nullable=False, default=datetime.now, comment="Created at"
)
lastSeen = Column(
DateTime, nullable=False, default=datetime.now, comment="Was online at"
)
deletedAt = Column(DateTime, nullable=True, comment="Deleted at")
links = Column(JSONType, nullable=True, comment="Links")
oauth = Column(String, nullable=True)
ratings = relationship(UserRating, foreign_keys=UserRating.user)
roles = relationship(lambda: Role, secondary=UserRole.__tablename__)
oid = Column(String, nullable=True)
@staticmethod
def init_table():
with local_session() as session:
default = session.query(User).filter(User.slug == "anonymous").first()
if not default:
default_dict = {
"email": "noreply@discours.io",
"username": "noreply@discours.io",
"name": "Аноним",
"slug": "anonymous",
}
default = User.create(**default_dict)
session.add(default)
discours_dict = {
"email": "welcome@discours.io",
"username": "welcome@discours.io",
"name": "Дискурс",
"slug": "discours",
}
discours = User.create(**discours_dict)
session.add(discours)
session.commit()
User.default_user = default
def get_permission(self):
scope = {}
for role in self.roles:
for p in role.permissions:
if p.resource not in scope:
scope[p.resource] = set()
scope[p.resource].add(p.operation)
print(scope)
return scope
# if __name__ == "__main__":
# print(User.get_permission(user_id=1)) # type: ignore