"""``sites.*`` — web domains, databases, database users. Footguns baked into these wrappers so callers don't have to rediscover them: * ``sites_web_domain_update``'s second positional arg is ``client_id``, **not** the primary id. For admin-owned sites pass ``client_id=0``. This is a common footgun — the ISPConfig remote API is inconsistent about this across methods. * ``sys_groupid=1`` means admin-owned. Pass it through unchanged on update calls or ISPConfig will reassign ownership. * ``fastcgi_php_version`` is the legacy field; newer installs use ``server_php_id`` (int, references ``server_php.server_php_id``). Set the one your panel version knows about — :meth:`SitesModule.enable_php` handles this. """ from __future__ import annotations import logging from collections.abc import Mapping from typing import TYPE_CHECKING, Any, cast from .types import Database, DatabaseUser, WebDomain if TYPE_CHECKING: from .client import ISPConfigClient log = logging.getLogger("ispconfig.sites") class SitesModule: def __init__(self, client: ISPConfigClient) -> None: self._c = client # ---- web domain --------------------------------------------------- def web_domain_get(self, primary_id: int) -> WebDomain: """Fetch a single ``web_domain`` row by its ``domain_id``.""" return cast(WebDomain, self._c._call("sites_web_domain_get", ("primary_id", int(primary_id)))) def web_domain_add(self, client_id: int, params: Mapping[str, Any], read_only: bool = False) -> int: """Create a new site. Returns the new ``domain_id``. ``client_id=0`` creates an admin-owned site. """ return int(self._c._call( "sites_web_domain_add", ("client_id", int(client_id)), ("params", dict(params)), ("read_only", bool(read_only)), )) def web_domain_update(self, client_id: int, primary_id: int, params: Mapping[str, Any]) -> int: """Update a site. .. warning:: The second positional arg is ``client_id``, not ``primary_id``. Pass 0 for admin-owned. See module docstring. """ return int(self._c._call( "sites_web_domain_update", ("client_id", int(client_id)), ("primary_id", int(primary_id)), ("params", dict(params)), )) def web_domain_delete(self, primary_id: int) -> int: return int(self._c._call("sites_web_domain_delete", ("primary_id", int(primary_id)))) def web_domain_set_status(self, primary_id: int, status: str) -> int: """``status`` is typically ``'active'`` or ``'inactive'``.""" return int(self._c._call( "sites_web_domain_set_status", ("primary_id", int(primary_id)), ("status", status), )) # ---- helpers ------------------------------------------------------ def enable_php( self, domain_id: int, mode: str = "fast-cgi", server_php_id: int = 0, pm: str = "ondemand", pm_max_children: int = 10, pm_start_servers: int = 2, pm_min_spare_servers: int = 1, pm_max_spare_servers: int = 5, ) -> int: """Flip on PHP with sane defaults. Covers the usual field soup: ``php`` (the mode), ``server_php_id`` (what PHP binary), and the ``pm_*`` family (only relevant for ``php-fpm`` / ``fast-cgi``). Preserves existing ``client_id`` / ``sys_groupid`` by reading the record first. """ current = self.web_domain_get(domain_id) client_id = int(current.get("sys_groupid", 0) or 0) # sys_groupid==1 => admin; pass 0 as client_id for update. if client_id == 1: client_id = 0 params: dict[str, Any] = { "php": mode, "server_php_id": int(server_php_id), "pm": pm, "pm_max_children": int(pm_max_children), "pm_start_servers": int(pm_start_servers), "pm_min_spare_servers": int(pm_min_spare_servers), "pm_max_spare_servers": int(pm_max_spare_servers), } return self.web_domain_update(client_id, domain_id, params) def enable_letsencrypt(self, domain_id: int) -> int: """Turn on SSL + Let's Encrypt + force-https in one shot.""" current = self.web_domain_get(domain_id) client_id = int(current.get("sys_groupid", 0) or 0) if client_id == 1: client_id = 0 params = { "ssl": "y", "ssl_letsencrypt": "y", "rewrite_to_https": "y", } return self.web_domain_update(client_id, domain_id, params) # ---- databases ---------------------------------------------------- def database_get(self, primary_id: int) -> Database: return cast(Database, self._c._call("sites_database_get", ("primary_id", int(primary_id)))) def database_add(self, client_id: int, params: Mapping[str, Any]) -> int: return int(self._c._call( "sites_database_add", ("client_id", int(client_id)), ("params", dict(params)), )) def database_delete(self, primary_id: int) -> int: return int(self._c._call("sites_database_delete", ("primary_id", int(primary_id)))) def database_user_get(self, primary_id: int) -> DatabaseUser: return cast(DatabaseUser, self._c._call("sites_database_user_get", ("primary_id", int(primary_id)))) def database_user_add(self, client_id: int, params: Mapping[str, Any]) -> int: return int(self._c._call( "sites_database_user_add", ("client_id", int(client_id)), ("params", dict(params)), )) def database_user_update(self, client_id: int, primary_id: int, params: Mapping[str, Any]) -> int: return int(self._c._call( "sites_database_user_update", ("client_id", int(client_id)), ("primary_id", int(primary_id)), ("params", dict(params)), ))