letsbe-orchestrator/app/playbooks/poste.py

112 lines
2.9 KiB
Python

"""Poste.io mail server deployment playbook.
Defines the steps required to:
1. Perform initial setup via Playwright automation (configure hostname, create admin account)
Tenant servers must have stacks and env templates under /opt/letsbe.
"""
import uuid
from typing import Any
from urllib.parse import urlparse
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.task import Task, TaskStatus
# LetsBe standard paths
POSTE_STACK_DIR = "/opt/letsbe/stacks/poste"
# =============================================================================
# Initial Setup via Playwright
# =============================================================================
def build_poste_initial_setup_step(
*,
base_url: str,
admin_email: str,
admin_password: str | None = None,
) -> dict[str, Any]:
"""
Build a PLAYWRIGHT task payload for Poste.io initial setup.
This configures the mail server hostname and creates the admin account
on a fresh Poste.io installation.
Args:
base_url: The base URL for Poste.io (e.g., "https://mail.example.com")
admin_email: Email address for the admin account (e.g., admin@example.com)
admin_password: Password for the admin account (auto-generated if None)
Returns:
Task payload dict with type="PLAYWRIGHT"
"""
# Extract domain from URL for allowlist
parsed = urlparse(base_url)
allowed_domain = parsed.netloc # e.g., "mail.example.com"
inputs: dict[str, Any] = {
"base_url": base_url,
"admin_email": admin_email,
}
# Only include password if provided - scenario will auto-generate if missing
if admin_password:
inputs["admin_password"] = admin_password
return {
"scenario": "poste_initial_setup",
"inputs": inputs,
"options": {
"allowed_domains": [allowed_domain],
},
"timeout": 120,
}
async def create_poste_initial_setup_task(
*,
db: AsyncSession,
tenant_id: uuid.UUID,
agent_id: uuid.UUID,
base_url: str,
admin_email: str,
admin_password: str | None = None,
) -> Task:
"""
Create and persist a PLAYWRIGHT task for Poste.io initial setup.
Args:
db: Async database session
tenant_id: UUID of the tenant
agent_id: UUID of the agent to assign the task to
base_url: The base URL for Poste.io
admin_email: Email address for the admin account
admin_password: Password for admin (auto-generated if None)
Returns:
The created Task object with type="PLAYWRIGHT"
"""
payload = build_poste_initial_setup_step(
base_url=base_url,
admin_email=admin_email,
admin_password=admin_password,
)
task = Task(
tenant_id=tenant_id,
agent_id=agent_id,
type="PLAYWRIGHT",
payload=payload,
status=TaskStatus.PENDING.value,
)
db.add(task)
await db.commit()
await db.refresh(task)
return task