Files
headquarter/apps/api/app/services/traefik.py
T
alex 6aea953734 feat(FN-010): implement code-server spawn service with Docker Compose
- Add SpawnService with container lifecycle (spawn/stop/status)
- Generate Docker Compose services from tool manifests
- Integrate Traefik label generation with subdomain routing
- Mount workspace, config, and SSH key volumes
- Add container status polling and health checks
- Enhance tool instance API with spawn/stop/start/status endpoints
- Add Traefik forwardAuth middleware for auth proxy
- Update code-server manifest with runtime configuration
2026-05-14 17:27:19 +02:00

102 lines
3.3 KiB
Python

class TraefikLabelGenerator:
def __init__(self, domain: str, entrypoint: str = "websecure"):
self.domain = domain
self.entrypoint = entrypoint
def generate_subdomain(
self,
tool_key: str,
project_slug: str,
user_slug: str,
) -> str:
return f"{tool_key}-{project_slug}-{user_slug}.{self.domain}"
def generate_labels(
self,
instance_id: str,
tool_key: str,
project_slug: str,
user_slug: str,
container_port: int,
network_name: str = "tools",
) -> dict[str, str]:
subdomain = self.generate_subdomain(tool_key, project_slug, user_slug)
router_name = f"tool-{instance_id[:8]}"
service_name = f"tool-{instance_id[:8]}"
labels: dict[str, str] = {}
labels["traefik.enable"] = "true"
labels[f"traefik.http.routers.{router_name}.rule"] = (
f"Host(`{subdomain}`)"
)
labels[f"traefik.http.routers.{router_name}.entrypoints"] = (
self.entrypoint
)
labels[f"traefik.http.routers.{router_name}.service"] = service_name
if self.entrypoint == "websecure":
labels[f"traefik.http.routers.{router_name}.tls"] = "true"
labels[
f"traefik.http.routers.{router_name}.tls.certresolver"
] = "letsencrypt"
labels[f"traefik.http.services.{service_name}.loadbalancer.server.port"] = (
str(container_port)
)
labels[f"traefik.http.services.{service_name}.loadbalancer.server.scheme"] = (
"http"
)
middleware_name = f"tool-{instance_id[:8]}-sec"
labels[
f"traefik.http.middlewares.{middleware_name}.headers.stsSeconds"
] = "31536000"
labels[
f"traefik.http.middlewares.{middleware_name}.headers.stsIncludeSubdomains"
] = "true"
labels[
f"traefik.http.middlewares.{middleware_name}.headers.forceStsHeader"
] = "true"
labels[
f"traefik.http.middlewares.{middleware_name}.headers.contentTypeNosniff"
] = "true"
labels[
f"traefik.http.middlewares.{middleware_name}.headers.browserXssFilter"
] = "true"
labels[
f"traefik.http.middlewares.{middleware_name}.headers.customFrameOptionsValue"
] = "SAMEORIGIN"
labels[f"traefik.http.routers.{router_name}.middlewares"] = middleware_name
labels["traefik.docker.network"] = network_name
return labels
def generate_forward_auth_labels(
self,
instance_id: str,
auth_url: str,
) -> dict[str, str]:
router_name = f"tool-{instance_id[:8]}"
middleware_name = f"tool-{instance_id[:8]}-auth"
return {
f"traefik.http.middlewares.{middleware_name}.forwardauth.address": auth_url,
f"traefik.http.middlewares.{middleware_name}.forwardauth.trustForwardHeader": "true",
f"traefik.http.routers.{router_name}.middlewares": middleware_name,
}
def generate_removal_labels(
self,
instance_id: str,
) -> dict[str, str]:
router_name = f"tool-{instance_id[:8]}"
return {
"traefik.enable": "false",
f"traefik.http.routers.{router_name}.rule": "",
}