feat: add workspace

This commit is contained in:
Artem Tsyrulnikov
2025-12-13 22:09:55 +03:00
parent 2fdd56c3e0
commit af32e2a507
55 changed files with 677 additions and 227 deletions

View File

@@ -19,6 +19,55 @@ class Postgres(DatabaseBase):
async def create_user(self, user: domain.User) -> None:
await user.save()
async def create_workspace(self, workspace: domain.Workspace) -> None:
await workspace.save()
async def update_workspace(self, workspace: domain.Workspace) -> None:
await workspace.save()
async def delete_workspace(self, workspace_id: uuid.UUID) -> None:
await domain.Workspace.filter(id=workspace_id).delete()
async def add_user_to_workspace(
self, workspace_id: uuid.UUID, user_id: uuid.UUID, *, is_owner: bool = False
) -> None:
await domain.WorkspaceUser.create(workspace_id=workspace_id, user_id=user_id, is_owner=is_owner)
async def get_user_workspaces(self, user_id: uuid.UUID) -> list[domain.WorkspaceUser]:
return (
await domain.WorkspaceUser.filter(user_id=user_id)
.prefetch_related('workspace')
.order_by('created_at')
.all()
)
async def get_workspace(self, workspace_id: uuid.UUID) -> domain.Workspace | None:
return await domain.Workspace.get_or_none(id=workspace_id)
async def get_workspace_for_user(self, workspace_id: uuid.UUID, user_id: uuid.UUID) -> domain.Workspace | None:
membership = (
await domain.WorkspaceUser.filter(workspace_id=workspace_id, user_id=user_id)
.prefetch_related('workspace')
.first()
)
return membership.workspace if membership else None
async def get_default_workspace_for_user(self, user_id: uuid.UUID) -> domain.Workspace | None:
membership = (
await domain.WorkspaceUser.filter(user_id=user_id)
.prefetch_related('workspace')
.order_by('created_at')
.first()
)
return membership.workspace if membership else None
async def get_workspace_membership(
self, workspace_id: uuid.UUID, user_id: uuid.UUID
) -> domain.WorkspaceUser | None:
return await domain.WorkspaceUser.get_or_none(workspace_id=workspace_id, user_id=user_id).prefetch_related(
'workspace'
)
async def create_login_token(self, login_token: domain.LoginToken) -> None:
await login_token.save()
@@ -46,12 +95,12 @@ class Postgres(DatabaseBase):
raise domain.LoginTokenAlreadyUsed() # либо нет токена, либо он уже использован
async def get_target_channel(
self, user_id: uuid.UUID, channel_id: uuid.UUID | None = None, telegram_id: int | None = None
self, workspace_id: uuid.UUID, channel_id: uuid.UUID | None = None, telegram_id: int | None = None
) -> domain.TargetChannel | None:
if not channel_id and not telegram_id:
raise ValueError('Either channel_id or telegram_id must be provided')
q = domain.TargetChannel.filter(user_id=user_id)
q = domain.TargetChannel.filter(workspace_id=workspace_id)
if channel_id:
q = q.filter(id=channel_id)
if telegram_id:
@@ -59,8 +108,17 @@ class Postgres(DatabaseBase):
return await q.first()
async def get_target_channel_for_user_by_telegram(
self, user_id: uuid.UUID, telegram_id: int
) -> domain.TargetChannel | None:
return (
await domain.TargetChannel.filter(telegram_id=telegram_id, workspace__workspace_users__user_id=user_id)
.prefetch_related('workspace')
.first()
)
async def upsert_target_channel(self, channel: domain.TargetChannel) -> None:
existing = await self.get_target_channel(user_id=channel.user_id, telegram_id=channel.telegram_id)
existing = await self.get_target_channel(workspace_id=channel.workspace_id, telegram_id=channel.telegram_id)
if not existing:
await channel.save()
@@ -68,22 +126,24 @@ class Postgres(DatabaseBase):
existing.title = channel.title
existing.username = channel.username
existing.user_id = channel.user_id
existing.workspace_id = channel.workspace_id
existing.status = channel.status
existing.deleted_at = None
existing.updated_at = timezone.now()
await existing.save()
async def get_user_target_channels(self, user_id: uuid.UUID) -> list[domain.TargetChannel]:
return await domain.TargetChannel.filter(user_id=user_id, status=domain.TargetChannelStatus.ACTIVE).all()
async def get_workspace_target_channels(self, workspace_id: uuid.UUID) -> list[domain.TargetChannel]:
return await domain.TargetChannel.filter(
workspace_id=workspace_id, status=domain.TargetChannelStatus.ACTIVE
).all()
async def update_target_channel(self, channel: domain.TargetChannel) -> None:
await channel.save()
async def get_external_channel(
self, user_id: uuid.UUID, channel_id: uuid.UUID | None = None, telegram_id: int | None = None
self, workspace_id: uuid.UUID, channel_id: uuid.UUID | None = None, telegram_id: int | None = None
) -> domain.ExternalChannel | None:
query = domain.ExternalChannel.filter(user_id=user_id)
query = domain.ExternalChannel.filter(workspace_id=workspace_id)
if channel_id:
query = query.filter(id=channel_id)
if telegram_id:
@@ -94,8 +154,8 @@ class Postgres(DatabaseBase):
async def get_external_channels_for_target(self, target_channel_id: uuid.UUID) -> list[domain.ExternalChannel]:
return await domain.ExternalChannel.filter(target_channels__id=target_channel_id).all()
async def get_user_external_channels(self, user_id: uuid.UUID) -> list[domain.ExternalChannel]:
return await domain.ExternalChannel.filter(user_id=user_id).all()
async def get_workspace_external_channels(self, workspace_id: uuid.UUID) -> list[domain.ExternalChannel]:
return await domain.ExternalChannel.filter(workspace_id=workspace_id).all()
async def add_external_channel_to_targets(
self, external_channel: domain.ExternalChannel, target_channel_ids: list[uuid.UUID]
@@ -121,16 +181,18 @@ class Postgres(DatabaseBase):
async def update_external_channel(self, channel: domain.ExternalChannel) -> None:
await channel.save()
async def get_creative(self, user_id: uuid.UUID, creative_id: uuid.UUID) -> domain.Creative | None:
return await domain.Creative.get_or_none(id=creative_id, user_id=user_id).prefetch_related('target_channel')
async def get_creative(self, workspace_id: uuid.UUID, creative_id: uuid.UUID) -> domain.Creative | None:
return await domain.Creative.get_or_none(id=creative_id, workspace_id=workspace_id).prefetch_related(
'target_channel'
)
async def update_creative(self, creative: domain.Creative) -> None:
await creative.save()
async def get_user_creatives(
self, user_id: uuid.UUID, target_channel_id: uuid.UUID | None = None, include_archived: bool = False
async def get_workspace_creatives(
self, workspace_id: uuid.UUID, target_channel_id: uuid.UUID | None = None, include_archived: bool = False
) -> list[domain.Creative]:
query = domain.Creative.filter(user_id=user_id)
query = domain.Creative.filter(workspace_id=workspace_id)
if target_channel_id:
query = query.filter(target_channel_id=target_channel_id)
@@ -143,8 +205,8 @@ class Postgres(DatabaseBase):
async def delete_creative(self, creative_id: uuid.UUID) -> None:
await domain.Creative.filter(id=creative_id).delete()
async def get_placement(self, user_id: uuid.UUID, placement_id: uuid.UUID) -> domain.Placement | None:
return await domain.Placement.get_or_none(id=placement_id, user_id=user_id).prefetch_related(
async def get_placement(self, workspace_id: uuid.UUID, placement_id: uuid.UUID) -> domain.Placement | None:
return await domain.Placement.get_or_none(id=placement_id, workspace_id=workspace_id).prefetch_related(
'target_channel', 'external_channel', 'creative'
)
@@ -154,15 +216,15 @@ class Postgres(DatabaseBase):
async def update_placement(self, placement: domain.Placement) -> None:
await placement.save()
async def get_user_placements(
async def get_workspace_placements(
self,
user_id: uuid.UUID,
workspace_id: uuid.UUID,
target_channel_id: uuid.UUID | None = None,
external_channel_id: uuid.UUID | None = None,
creative_id: uuid.UUID | None = None,
include_archived: bool = False,
) -> list[domain.Placement]:
query = domain.Placement.filter(user_id=user_id)
query = domain.Placement.filter(workspace_id=workspace_id)
if target_channel_id:
query = query.filter(target_channel_id=target_channel_id)