"""Read-only alias projection from Hub-owned persistence.""" from __future__ import annotations import re from collections import defaultdict from typing import Any, cast from urllib.parse import urlsplit from pydantic import AnyHttpUrl, TypeAdapter from sqlalchemy import select from sqlalchemy.orm import Session from packages.hub_contracts.dataplane import ( SUPPORTED_PROTOCOLS, AliasRecord, ClientBinding, EnvironmentName, ) from .models import ( ClientBindingRow, ClientRow, EnvironmentRow, ProjectRow, VirtualServerRow, ) class SqlAlchemyAliasSource: """Projects only enabled Hub virtual servers and active explicit bindings.""" def __init__(self, session: Session, *, workspace_id: Any, gateway_base_url: str) -> None: self.session = session self.workspace_id = workspace_id base = TypeAdapter(AnyHttpUrl).validate_python(gateway_base_url) internal_contextforge = ( base.scheme == "http" and base.host in {"contextforge", "contextforge-control"} and base.port == 4444 and (base.path or "").rstrip("/") == "/mcp" ) if ( base.username is not None or base.password is not None or (base.scheme != "https" and not internal_contextforge) ): raise ValueError("gateway URL must be HTTPS or the fixed internal gateway") self.gateway_base_url = str(base).rstrip("/") self.internal_contextforge = internal_contextforge def aliases(self) -> tuple[AliasRecord, ...]: projects = { row.id: row for row in self.session.scalars( select(ProjectRow).where(ProjectRow.workspace_id == self.workspace_id) ).all() } environments = { row.id: row for row in self.session.scalars( select(EnvironmentRow).where(EnvironmentRow.workspace_id == self.workspace_id) ).all() } clients = { row.id: row for row in self.session.scalars( select(ClientRow).where(ClientRow.workspace_id == self.workspace_id) ).all() } bindings_by_server: dict[Any, list[ClientBinding]] = defaultdict(list) for row in self.session.scalars( select(ClientBindingRow).where( ClientBindingRow.workspace_id == self.workspace_id, ClientBindingRow.enabled.is_(True), ) ).all(): client = clients.get(row.client_id) if client is None or client.status != "active": continue bindings_by_server[row.virtual_server_id].append( ClientBinding( client_id=client.id, issuer=client.issuer, subject=client.subject, enabled=True, ) ) values: list[AliasRecord] = [] for server in self.session.scalars( select(VirtualServerRow).where( VirtualServerRow.workspace_id == self.workspace_id, VirtualServerRow.enabled.is_(True), VirtualServerRow.health_state.in_(("healthy", "degraded")), ) ).all(): if self.internal_contextforge and not server.gateway_external_id: continue project = projects.get(server.project_id) environment = environments.get(server.environment_id) if project is None or environment is None: continue if environment.name not in {"dev", "test", "staging", "prod"}: continue metadata = server.record_metadata endpoint_alias = metadata.get("endpointAlias") if isinstance(metadata, dict) else None if not isinstance(endpoint_alias, str): endpoint_alias = f"/mcp/{project.slug}/{environment.name}" try: if self.internal_contextforge: external_id = server.gateway_external_id or "" if not re.fullmatch(r"[0-9a-f]{32}", external_id): raise ValueError("invalid ContextForge server route identifier") parsed = urlsplit(self.gateway_base_url) upstream = f"{parsed.scheme}://{parsed.netloc}/servers/{external_id}/mcp" else: upstream = f"{self.gateway_base_url}{endpoint_alias}" values.append( AliasRecord( project_slug=project.slug, workspace_id=self.workspace_id, project_id=project.id, environment=cast(EnvironmentName, environment.name), alias=endpoint_alias, upstream_url=TypeAdapter(AnyHttpUrl).validate_python(upstream), enabled=True, protocol_revisions=tuple(SUPPORTED_PROTOCOLS), bindings=tuple(bindings_by_server.get(server.id, ())), ) ) except ValueError: continue return tuple(sorted(values, key=lambda item: (item.project_slug, item.environment)))