"""Publishing / SEO / PWA repositories — Phase 11.7.""" from __future__ import annotations from typing import Sequence from uuid import UUID from sqlalchemy import func, select from app.models.publishing import ( DomainEdgeBinding, PublishRelease, PwaManifest, RobotsShell, SeoMetadata, SitemapShell, ) from app.models.types import SeoTarget from app.repositories.base import TenantBaseRepository async def _list_filtered(session, model, tenant_id, offset, limit, spec): base = select(model).where( model.tenant_id == tenant_id, model.is_deleted.is_(False), ) count_base = select(func.count()).select_from(model).where( model.tenant_id == tenant_id, model.is_deleted.is_(False), ) if spec is not None: base = spec.apply(base) for clause in spec.filter_clauses(): count_base = count_base.where(clause) else: base = base.order_by(model.created_at.desc()) total = int((await session.execute(count_base)).scalar_one()) result = await session.execute(base.offset(offset).limit(limit)) return result.scalars().all(), total class PublishReleaseRepository(TenantBaseRepository[PublishRelease]): model = PublishRelease async def list_filtered(self, tenant_id, *, offset, limit, spec=None): return await _list_filtered( self.session, self.model, tenant_id, offset, limit, spec ) class DomainEdgeBindingRepository(TenantBaseRepository[DomainEdgeBinding]): model = DomainEdgeBinding async def get_by_site_domain( self, tenant_id: UUID, site_domain_id: UUID ) -> DomainEdgeBinding | None: stmt = select(self.model).where( self.model.tenant_id == tenant_id, self.model.site_domain_id == site_domain_id, self.model.is_deleted.is_(False), ) return (await self.session.execute(stmt)).scalar_one_or_none() async def list_filtered(self, tenant_id, *, offset, limit, spec=None): return await _list_filtered( self.session, self.model, tenant_id, offset, limit, spec ) class SeoMetadataRepository(TenantBaseRepository[SeoMetadata]): model = SeoMetadata async def get_by_target( self, tenant_id: UUID, target_type: SeoTarget, target_id: UUID ) -> SeoMetadata | None: stmt = select(self.model).where( self.model.tenant_id == tenant_id, self.model.target_type == target_type, self.model.target_id == target_id, self.model.is_deleted.is_(False), ) return (await self.session.execute(stmt)).scalar_one_or_none() async def list_filtered(self, tenant_id, *, offset, limit, spec=None): return await _list_filtered( self.session, self.model, tenant_id, offset, limit, spec ) class SitemapShellRepository(TenantBaseRepository[SitemapShell]): model = SitemapShell async def get_by_site( self, tenant_id: UUID, site_id: UUID ) -> SitemapShell | None: stmt = select(self.model).where( self.model.tenant_id == tenant_id, self.model.site_id == site_id, self.model.is_deleted.is_(False), ) return (await self.session.execute(stmt)).scalar_one_or_none() async def list_filtered(self, tenant_id, *, offset, limit, spec=None): return await _list_filtered( self.session, self.model, tenant_id, offset, limit, spec ) class RobotsShellRepository(TenantBaseRepository[RobotsShell]): model = RobotsShell async def get_by_site(self, tenant_id: UUID, site_id: UUID) -> RobotsShell | None: stmt = select(self.model).where( self.model.tenant_id == tenant_id, self.model.site_id == site_id, self.model.is_deleted.is_(False), ) return (await self.session.execute(stmt)).scalar_one_or_none() async def list_filtered(self, tenant_id, *, offset, limit, spec=None): return await _list_filtered( self.session, self.model, tenant_id, offset, limit, spec ) class PwaManifestRepository(TenantBaseRepository[PwaManifest]): model = PwaManifest async def get_by_site(self, tenant_id: UUID, site_id: UUID) -> PwaManifest | None: stmt = select(self.model).where( self.model.tenant_id == tenant_id, self.model.site_id == site_id, self.model.is_deleted.is_(False), ) return (await self.session.execute(stmt)).scalar_one_or_none() async def list_filtered(self, tenant_id, *, offset, limit, spec=None): return await _list_filtered( self.session, self.model, tenant_id, offset, limit, spec )