---
title: Sitios y servidores
description: Alcance personal y referencia de las rutas existentes de sitios y servidores pendientes de adaptación.
sidebar:
  label: Sitios y servidores
  order: 1
---

La referencia pública se limita a sitios y servidores. En el acceso personal, cada usuario solo podrá ver y operar sobre los recursos asociados a su cuenta y autorizados por sus permisos. La gestión de usuarios y paquetes no forma parte del contrato personal.

:::note
**Adaptación pendiente.** Las rutas que siguen corresponden al código administrativo existente en la revisión `9299a05`, analizada el 21 de septiembre de 2026. Requieren la clave global administrativa descrita en [autenticación y convenciones](/api). Todavía no aceptan claves personales ni implementan el aislamiento exigido para usuarios. Los ejemplos describen ese contrato existente, no instrucciones para utilizar una clave personal.
:::

## Requisitos del acceso personal

- Listar únicamente los sitios y servidores asociados al usuario identificado por el token.
- Aplicar el mismo control al consultar un identificador concreto, aunque no proceda de un listado.
- Calcular la paginación, los totales y los datos relacionados únicamente sobre recursos autorizados.
- Combinar permisos del usuario, permisos del token y acceso al recurso.
- Derivar la identidad del token; no permitir seleccionar otro propietario mediante `user_id`.
- Rechazar las claves personales en las operaciones administrativas de usuarios y paquetes.

Estos requisitos deben implementarse y probarse en el backend antes de ofrecer el acceso personal.

Los cuerpos siguientes son ejemplos, no operaciones ejecutadas. Los identificadores deben existir en la instalación destino.

## Rutas administrativas existentes de estos recursos

| Método | Ruta | Respuesta correcta |
| --- | --- | --- |
| `GET` | `/api/sites` | `200`, listado paginado |
| `POST` | `/api/sites` | `201`, sitio en `data` |
| `GET` | `/api/sites/{site}` | `200`, sitio en `data` |
| `POST` | `/api/servers` | `201`, servidor en `data` |

Los parámetros de ruta son identificadores locales del panel, no identificadores de Ploi.

## Sitios

### Listar y consultar

`GET /api/sites` pagina `Site::paginate()` sin filtrado por usuario ni orden explícito. `GET /api/sites/{site}` devuelve el sitio identificado sin una comprobación de pertenencia al solicitante.

Ese comportamiento administrativo debe sustituirse en el acceso personal por consultas desde los sitios asociados al usuario. Filtrar únicamente la interfaz del CLI no cumple el requisito.

Ejemplo de estructura de respuesta individual:

```json
{
  "data": {
    "id": 42,
    "status": "active",
    "server_id": 7,
    "domain": "example.com",
    "user_id": 3,
    "created_at": "2026-09-21T12:00:00.000000Z"
  }
}
```

`SiteData` limita la salida a esos seis campos. No devuelve host SSH, puerto, usuario del sistema, ruta de WordPress ni credenciales. Aunque el DTO expone un `user_id`, la asociación real de usuarios y sitios en el panel es de varios a varios mediante `user_service`.

### Crear

`POST /api/sites` requiere:

| Campo | Validación/comportamiento |
| --- | --- |
| `server_id` | Entero, servidor existente |
| `domain` | Cadena validada por `Hostname` y `ValidateMaximumSites` |
| `user_id` | Entero, usuario existente al que se asignará el sitio |

```json
{
  "server_id": 7,
  "domain": "nuevo.example.com",
  "user_id": 3
}
```

`CreateSiteAction` busca el servidor en los servidores asociados al `user_id` indicado. El límite de sitios del paquete se valida; un máximo de cero representa sitios ilimitados en esa regla. Se crea el registro local, se asocia al usuario y se despacha el trabajo de provisión. Un `201` no garantiza que la provisión remota haya finalizado.

El usuario destino lo elige la petición administrativa, no el Bearer token. Campos como `status` no se toman de la entrada del controlador.

### Comportamientos que requieren atención

- La API exige `server_id`; la selección automática sin servidor explícito de la acción compartida no está expuesta por este contrato.
- Las comprobaciones de servidor activo y dominio ya existente del controlador web no se repiten explícitamente en el controlador API.
- El controlador contiene una respuesta `422` para falta de capacidad, pero llama a `$site->refresh()` antes de comprobar si la acción devolvió `null`. Esa rama no garantiza una respuesta controlada si se alcanza ese caso.

Fuente: `app/Http/Controllers/Api/SiteController.php`, `app/DataTransferObjects/SiteData.php`, `app/Rules/ValidateMaximumSites.php` y `app/Actions/Site/CreateSiteAction.php`.

## Servidores

### Crear

`POST /api/servers` es la única operación de servidores expuesta en `routes/api.php`.

| Campo | Obligatorio | Validación/comportamiento |
| --- | --- | --- |
| `name` | Sí | Cadena alfanumérica con guiones/guiones bajos, máximo 40 caracteres |
| `provider_id` | Sí | Proveedor existente, distinto de cero |
| `provider_region_id` | Sí | Región existente, distinta de cero |
| `provider_plan_id` | Sí | Plan existente, distinto de cero |
| `database_type` | Sí | `none`, `mysql`, `mariadb`, `postgresql` |
| `user_id` | Sí | Usuario existente |
| `php_version` | No | Valor permitido; predeterminado `8.4` |
| `os_type` | No | Valor permitido; predeterminado `ubuntu-24-04-lts` |

Valores PHP aceptados por el modelo revisado: `none`, `5.6`, `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, `8.0`, `8.1`, `8.2`, `8.3`, `8.4`, `8.5`.

Sistemas operativos aceptados: `ubuntu-22-04-lts`, `ubuntu-24-04-lts`, `ubuntu-26-04-lts`. Son valores permitidos por la validación local, no una garantía de disponibilidad en cada proveedor remoto.

```json
{
  "name": "servidor-ejemplo",
  "provider_id": 1,
  "provider_region_id": 2,
  "provider_plan_id": 3,
  "database_type": "mysql",
  "php_version": "8.4",
  "os_type": "ubuntu-24-04-lts",
  "user_id": 3
}
```

El controlador fija `type=server` e `install_monitoring=true`. La acción comprueba que el proveedor pertenezca al paquete del usuario y que región y plan pertenezcan a ese proveedor. Para este tipo de servidor, `database_type=none` se transforma en `mysql` y `php_version=none` en `8.4`.

Se crea inicialmente un servidor `busy` y se despacha el trabajo de provisión. La respuesta es `201` con `data` construido desde `ServerData` tras refrescar el modelo; el estado observado depende también del procesamiento del trabajo.

Campos definidos en `ServerData`: `id`, `status`, `name`, `provider_id`, `provider_region_id`, `provider_plan_id`, `type`, `database_type`, `php_version`, `os_type`, `install_monitoring`, `user_id`, `created_at`.

Hay una discrepancia a verificar: el modelo almacena el sistema operativo como `os`, mientras el DTO utiliza `os_type` y un valor predeterminado. La respuesta no debe asumirse como confirmación fiel del SO solicitado sin una prueba específica.

### Límites de este contrato

- No hay `GET /api/servers` ni `GET /api/servers/{id}` para descubrir servidores o seguir su provisión.
- No hay endpoints API para descubrir los identificadores de proveedor, región y plan requeridos.
- El controlador API no replica explícitamente las comprobaciones de permiso de creación y límite de servidores del controlador web.
- La prueba de creación correcta en `ServerControllerTest.php` está marcada con `skip(true)`. Su estructura esperada también difiere del DTO actual. Se documenta el código, sin presentar esa prueba omitida como validación de funcionamiento.

Fuente: `app/Http/Controllers/Api/ServerController.php`, `app/DataTransferObjects/ServerData.php`, `app/Actions/Server/CreateServerAction.php` y `app/Models/Server.php`.

## Relación con el futuro CLI

Estos endpoints tienen un contrato administrativo global. Añadir nuevas claves válidas al middleware actual no los convierte en endpoints seguros por usuario. La [propuesta de conexión](/cli/connect) requiere identidad, filtrado de recursos y permisos propios antes de distribuir tokens personales.
