|
import enum |
|
import json |
|
|
|
from flask_login import UserMixin |
|
|
|
from extensions.ext_database import db |
|
|
|
from .types import StringUUID |
|
|
|
|
|
class AccountStatus(str, enum.Enum): |
|
PENDING = "pending" |
|
UNINITIALIZED = "uninitialized" |
|
ACTIVE = "active" |
|
BANNED = "banned" |
|
CLOSED = "closed" |
|
|
|
|
|
class Account(UserMixin, db.Model): |
|
__tablename__ = "accounts" |
|
__table_args__ = (db.PrimaryKeyConstraint("id", name="account_pkey"), db.Index("account_email_idx", "email")) |
|
|
|
id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()")) |
|
name = db.Column(db.String(255), nullable=False) |
|
email = db.Column(db.String(255), nullable=False) |
|
password = db.Column(db.String(255), nullable=True) |
|
password_salt = db.Column(db.String(255), nullable=True) |
|
avatar = db.Column(db.String(255)) |
|
interface_language = db.Column(db.String(255)) |
|
interface_theme = db.Column(db.String(255)) |
|
timezone = db.Column(db.String(255)) |
|
last_login_at = db.Column(db.DateTime) |
|
last_login_ip = db.Column(db.String(255)) |
|
last_active_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) |
|
status = db.Column(db.String(16), nullable=False, server_default=db.text("'active'::character varying")) |
|
initialized_at = db.Column(db.DateTime) |
|
created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) |
|
updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) |
|
|
|
@property |
|
def is_password_set(self): |
|
return self.password is not None |
|
|
|
@property |
|
def current_tenant(self): |
|
return self._current_tenant |
|
|
|
@current_tenant.setter |
|
def current_tenant(self, value: "Tenant"): |
|
tenant = value |
|
ta = TenantAccountJoin.query.filter_by(tenant_id=tenant.id, account_id=self.id).first() |
|
if ta: |
|
tenant.current_role = ta.role |
|
else: |
|
tenant = None |
|
self._current_tenant = tenant |
|
|
|
@property |
|
def current_tenant_id(self): |
|
return self._current_tenant.id |
|
|
|
@current_tenant_id.setter |
|
def current_tenant_id(self, value: str): |
|
try: |
|
tenant_account_join = ( |
|
db.session.query(Tenant, TenantAccountJoin) |
|
.filter(Tenant.id == value) |
|
.filter(TenantAccountJoin.tenant_id == Tenant.id) |
|
.filter(TenantAccountJoin.account_id == self.id) |
|
.one_or_none() |
|
) |
|
|
|
if tenant_account_join: |
|
tenant, ta = tenant_account_join |
|
tenant.current_role = ta.role |
|
else: |
|
tenant = None |
|
except: |
|
tenant = None |
|
|
|
self._current_tenant = tenant |
|
|
|
@property |
|
def current_role(self): |
|
return self._current_tenant.current_role |
|
|
|
def get_status(self) -> AccountStatus: |
|
status_str = self.status |
|
return AccountStatus(status_str) |
|
|
|
@classmethod |
|
def get_by_openid(cls, provider: str, open_id: str) -> db.Model: |
|
account_integrate = ( |
|
db.session.query(AccountIntegrate) |
|
.filter(AccountIntegrate.provider == provider, AccountIntegrate.open_id == open_id) |
|
.one_or_none() |
|
) |
|
if account_integrate: |
|
return db.session.query(Account).filter(Account.id == account_integrate.account_id).one_or_none() |
|
return None |
|
|
|
def get_integrates(self) -> list[db.Model]: |
|
ai = db.Model |
|
return db.session.query(ai).filter(ai.account_id == self.id).all() |
|
|
|
|
|
@property |
|
def is_admin_or_owner(self): |
|
return TenantAccountRole.is_privileged_role(self._current_tenant.current_role) |
|
|
|
@property |
|
def is_editor(self): |
|
return TenantAccountRole.is_editing_role(self._current_tenant.current_role) |
|
|
|
@property |
|
def is_dataset_editor(self): |
|
return TenantAccountRole.is_dataset_edit_role(self._current_tenant.current_role) |
|
|
|
@property |
|
def is_dataset_operator(self): |
|
return self._current_tenant.current_role == TenantAccountRole.DATASET_OPERATOR |
|
|
|
|
|
class TenantStatus(str, enum.Enum): |
|
NORMAL = "normal" |
|
ARCHIVE = "archive" |
|
|
|
|
|
class TenantAccountRole(str, enum.Enum): |
|
OWNER = "owner" |
|
ADMIN = "admin" |
|
EDITOR = "editor" |
|
NORMAL = "normal" |
|
DATASET_OPERATOR = "dataset_operator" |
|
|
|
@staticmethod |
|
def is_valid_role(role: str) -> bool: |
|
return role and role in { |
|
TenantAccountRole.OWNER, |
|
TenantAccountRole.ADMIN, |
|
TenantAccountRole.EDITOR, |
|
TenantAccountRole.NORMAL, |
|
TenantAccountRole.DATASET_OPERATOR, |
|
} |
|
|
|
@staticmethod |
|
def is_privileged_role(role: str) -> bool: |
|
return role and role in {TenantAccountRole.OWNER, TenantAccountRole.ADMIN} |
|
|
|
@staticmethod |
|
def is_non_owner_role(role: str) -> bool: |
|
return role and role in { |
|
TenantAccountRole.ADMIN, |
|
TenantAccountRole.EDITOR, |
|
TenantAccountRole.NORMAL, |
|
TenantAccountRole.DATASET_OPERATOR, |
|
} |
|
|
|
@staticmethod |
|
def is_editing_role(role: str) -> bool: |
|
return role and role in {TenantAccountRole.OWNER, TenantAccountRole.ADMIN, TenantAccountRole.EDITOR} |
|
|
|
@staticmethod |
|
def is_dataset_edit_role(role: str) -> bool: |
|
return role and role in { |
|
TenantAccountRole.OWNER, |
|
TenantAccountRole.ADMIN, |
|
TenantAccountRole.EDITOR, |
|
TenantAccountRole.DATASET_OPERATOR, |
|
} |
|
|
|
|
|
class Tenant(db.Model): |
|
__tablename__ = "tenants" |
|
__table_args__ = (db.PrimaryKeyConstraint("id", name="tenant_pkey"),) |
|
|
|
id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()")) |
|
name = db.Column(db.String(255), nullable=False) |
|
encrypt_public_key = db.Column(db.Text) |
|
plan = db.Column(db.String(255), nullable=False, server_default=db.text("'basic'::character varying")) |
|
status = db.Column(db.String(255), nullable=False, server_default=db.text("'normal'::character varying")) |
|
custom_config = db.Column(db.Text) |
|
created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) |
|
updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) |
|
|
|
def get_accounts(self) -> list[Account]: |
|
return ( |
|
db.session.query(Account) |
|
.filter(Account.id == TenantAccountJoin.account_id, TenantAccountJoin.tenant_id == self.id) |
|
.all() |
|
) |
|
|
|
@property |
|
def custom_config_dict(self) -> dict: |
|
return json.loads(self.custom_config) if self.custom_config else {} |
|
|
|
@custom_config_dict.setter |
|
def custom_config_dict(self, value: dict): |
|
self.custom_config = json.dumps(value) |
|
|
|
|
|
class TenantAccountJoinRole(enum.Enum): |
|
OWNER = "owner" |
|
ADMIN = "admin" |
|
NORMAL = "normal" |
|
DATASET_OPERATOR = "dataset_operator" |
|
|
|
|
|
class TenantAccountJoin(db.Model): |
|
__tablename__ = "tenant_account_joins" |
|
__table_args__ = ( |
|
db.PrimaryKeyConstraint("id", name="tenant_account_join_pkey"), |
|
db.Index("tenant_account_join_account_id_idx", "account_id"), |
|
db.Index("tenant_account_join_tenant_id_idx", "tenant_id"), |
|
db.UniqueConstraint("tenant_id", "account_id", name="unique_tenant_account_join"), |
|
) |
|
|
|
id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()")) |
|
tenant_id = db.Column(StringUUID, nullable=False) |
|
account_id = db.Column(StringUUID, nullable=False) |
|
current = db.Column(db.Boolean, nullable=False, server_default=db.text("false")) |
|
role = db.Column(db.String(16), nullable=False, server_default="normal") |
|
invited_by = db.Column(StringUUID, nullable=True) |
|
created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) |
|
updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) |
|
|
|
|
|
class AccountIntegrate(db.Model): |
|
__tablename__ = "account_integrates" |
|
__table_args__ = ( |
|
db.PrimaryKeyConstraint("id", name="account_integrate_pkey"), |
|
db.UniqueConstraint("account_id", "provider", name="unique_account_provider"), |
|
db.UniqueConstraint("provider", "open_id", name="unique_provider_open_id"), |
|
) |
|
|
|
id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()")) |
|
account_id = db.Column(StringUUID, nullable=False) |
|
provider = db.Column(db.String(16), nullable=False) |
|
open_id = db.Column(db.String(255), nullable=False) |
|
encrypted_token = db.Column(db.String(255), nullable=False) |
|
created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) |
|
updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) |
|
|
|
|
|
class InvitationCode(db.Model): |
|
__tablename__ = "invitation_codes" |
|
__table_args__ = ( |
|
db.PrimaryKeyConstraint("id", name="invitation_code_pkey"), |
|
db.Index("invitation_codes_batch_idx", "batch"), |
|
db.Index("invitation_codes_code_idx", "code", "status"), |
|
) |
|
|
|
id = db.Column(db.Integer, nullable=False) |
|
batch = db.Column(db.String(255), nullable=False) |
|
code = db.Column(db.String(32), nullable=False) |
|
status = db.Column(db.String(16), nullable=False, server_default=db.text("'unused'::character varying")) |
|
used_at = db.Column(db.DateTime) |
|
used_by_tenant_id = db.Column(StringUUID) |
|
used_by_account_id = db.Column(StringUUID) |
|
deprecated_at = db.Column(db.DateTime) |
|
created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) |
|
|