feat: права и инвайты

This commit is contained in:
Artem Tsyrulnikov
2025-12-14 13:49:25 +03:00
parent f3e09a08eb
commit 4c3873c727
43 changed files with 1111 additions and 127 deletions

View File

@@ -44,15 +44,26 @@ from .subscription.handle_subscription import handle_subscription
from .subscription.handle_unsubscription import handle_unsubscription
from .views.get_views_history import get_views_history
from .workspace.create_workspace import create_workspace
from .workspace.create_workspace_invite import create_workspace_invite
from .workspace.delete_workspace import delete_workspace
from .workspace.get_workspace_invites import get_workspace_invites
from .workspace.get_workspace_members import get_workspace_members
from .workspace.get_workspaces import get_workspaces
from .workspace.tg_accept_workspace_invite import tg_accept_workspace_invite
from .workspace.update_workspace import update_workspace
from .workspace.update_workspace_member_permissions import update_workspace_member_permissions
class Database(typing.Protocol):
def transaction(self) -> typing.AsyncContextManager[None]: ...
async def get_user(self, user_id: UUID | None = None, telegram_id: int | None = None) -> domain.User | None: ...
async def get_user(
self,
user_id: UUID | None = None,
telegram_id: int | None = None,
) -> domain.User | None: ...
async def get_user_by_username(self, username: str) -> domain.User | None: ...
async def create_user(self, user: domain.User) -> None: ...
@@ -62,7 +73,16 @@ class Database(typing.Protocol):
async def delete_workspace(self, workspace_id: UUID) -> None: ...
async def add_user_to_workspace(self, workspace_id: UUID, user_id: UUID) -> None: ...
async def add_user_to_workspace(self, workspace_id: UUID, user_id: UUID) -> domain.WorkspaceUser: ...
async def set_workspace_user_permissions(
self,
workspace_user_id: UUID,
global_permissions: set[domain.PermissionKey],
scoped_permissions: list[tuple[domain.PermissionKey, domain.PermissionScopeType, UUID]],
) -> None: ...
async def update_workspace_user(self, workspace_user: domain.WorkspaceUser) -> None: ...
async def get_user_workspaces(self, user_id: UUID) -> list[domain.WorkspaceUser]: ...
@@ -74,6 +94,22 @@ class Database(typing.Protocol):
async def get_workspace_membership(self, workspace_id: UUID, user_id: UUID) -> domain.WorkspaceUser | None: ...
async def get_workspace_members(self, workspace_id: UUID) -> list[domain.WorkspaceUser]: ...
async def get_workspace_member(self, workspace_user_id: UUID) -> domain.WorkspaceUser | None: ...
async def create_workspace_invite(self, invite: domain.WorkspaceInvite) -> None: ...
async def update_workspace_invite(self, invite: domain.WorkspaceInvite) -> None: ...
async def get_workspace_invite(self, invite_id: UUID) -> domain.WorkspaceInvite | None: ...
async def get_workspace_invite_by_user(
self, workspace_id: UUID, user_id: UUID
) -> domain.WorkspaceInvite | None: ...
async def get_workspace_invites(self, workspace_id: UUID) -> list[domain.WorkspaceInvite]: ...
async def create_login_token(self, login_token: domain.LoginToken) -> None: ...
async def create_creative(self, creative: domain.Creative) -> None: ...
@@ -116,7 +152,9 @@ class Database(typing.Protocol):
async def update_project(self, project: domain.Project) -> None: ...
async def get_workspace_projects(self, workspace_id: UUID) -> list[domain.Project]: ...
async def get_workspace_projects(
self, workspace_id: UUID, allowed_project_ids: set[UUID] | None = None
) -> list[domain.Project]: ...
async def get_active_purchase_plan(self, project_id: UUID) -> domain.PurchasePlan | None: ...
@@ -149,7 +187,11 @@ class Database(typing.Protocol):
async def update_creative(self, creative: domain.Creative) -> None: ...
async def get_workspace_creatives(
self, workspace_id: UUID, project_id: UUID | None = None, include_archived: bool = False
self,
workspace_id: UUID,
project_id: UUID | None = None,
include_archived: bool = False,
allowed_project_ids: set[UUID] | None = None,
) -> list[domain.Creative]: ...
async def delete_creative(self, creative_id: UUID) -> None: ...
@@ -167,6 +209,7 @@ class Database(typing.Protocol):
placement_channel_id: UUID | None = None,
creative_id: UUID | None = None,
include_archived: bool = False,
allowed_project_ids: set[UUID] | None = None,
) -> list[domain.Placement]: ...
async def delete_placement(self, placement_id: UUID) -> None: ...
@@ -239,6 +282,34 @@ class Usecase:
return workspace
async def ensure_workspace_permission(
self,
workspace_id: UUID,
user_id: UUID,
permission: domain.PermissionKey,
*,
for_project_id: UUID | None = None,
) -> domain.WorkspacePermissionContext:
membership = await self.database.get_workspace_membership(workspace_id, user_id)
if not membership or not membership.workspace:
raise domain.WorkspaceNotFound(workspace_id)
permissions = domain.WorkspacePermissions.from_membership(membership)
if for_project_id is not None:
has_permission = permissions.has_permission(
permission,
scope_type=domain.PermissionScopeType.PROJECT,
scope_id=for_project_id,
)
else:
has_permission = permissions.has_any(permission)
if not has_permission:
raise domain.WorkspaceAccessDenied(workspace_id)
return domain.build_workspace_permission_context(membership)
async def get_or_create_personal_workspace(self, user: domain.User) -> domain.Workspace:
workspace = await self.database.get_default_workspace_for_user(user.id)
if workspace:
@@ -249,7 +320,12 @@ class Usecase:
async with self.database.transaction():
await self.database.create_workspace(workspace)
await self.database.add_user_to_workspace(workspace.id, user.id)
membership = await self.database.add_user_to_workspace(workspace.id, user.id)
await self.database.set_workspace_user_permissions(
membership.id,
global_permissions={domain.PermissionKey.ADMIN_FULL},
scoped_permissions=[],
)
return workspace
@@ -301,3 +377,8 @@ class Usecase:
create_workspace = create_workspace
update_workspace = update_workspace
delete_workspace = delete_workspace
get_workspace_members = get_workspace_members
update_workspace_member_permissions = update_workspace_member_permissions
create_workspace_invite = create_workspace_invite
get_workspace_invites = get_workspace_invites
tg_accept_workspace_invite = tg_accept_workspace_invite