# WordPress

O template `wordpress` instancia, de uma vez, um banco MariaDB gerenciado e o serviço WordPress ligado a ele. Você não escreve Dockerfile nem variáveis de banco.

## Pré-requisitos

- Token com `projects:write`, `templates:deploy`, `deploy` e `logs:read` ([Tokens e escopos](/seguranca/tokens-e-escopos/)).
- Mínimo de **512 MB e 1 vCPU** para o serviço (recomendado 2 vCPU para tráfego real). Confira o que o seu plano permite em `GET /v1/limits`.

## O que o template configura

Serviços criados (nomes fixos):

| Serviço | Tipo | Detalhes |
|---|---|---|
| `db` | `database`, motor `mariadb` | cota de 1024 MB |
| `web` | `app`, público | imagem `wordpress:6.9.4-php8.3-apache` fixada por digest, porta 8080, volume `wp-content` de 5 GB em `/var/www/html/wp-content` |

O `web` que todo projeto novo já traz **não precisa ser apagado**: se ainda estiver virgem (sem deploy, variáveis, domínio ou volume), o template o **adota** (o `id` do serviço é o mesmo) e o configura. Se o `web` já foi usado, a instanciação responde `409 name_taken` e nada é criado.

Como o WordPress roda na plataforma:

- Roda como `33:33` (`www-data`), sistema de arquivos **somente leitura**; tmpfs em `/var/www/html` (128 MB), `/tmp`, `/var/run/apache2` e `/var/lock/apache2`. Só `wp-content` (o volume) persiste: plugins, temas e uploads ficam lá.
- As variáveis de banco vêm por referência (`WORDPRESS_DB_HOST=${{db.MYSQL_HOST}}:${{db.MYSQL_PORT}}`, `..._USER`, `..._PASSWORD`, `..._NAME`); a conexão usa TLS com verificação do certificado do servidor.
- As chaves e *salts* (`WORDPRESS_AUTH_KEY` etc.) são geradas por instância com `{{generate:secret64}}` (CSPRNG) e gravadas como variáveis **sensíveis**.
- O `wp-cron` do WordPress é desligado (`DISABLE_WP_CRON`) e a plataforma faz um `GET /wp-cron.php?doing_wp_cron` interno a cada 5 minutos pela rede do grupo (sem shell, sem comando arbitrário).
- O proxy responde `404` para `/xmlrpc.php`, `/readme.html` e `/license.txt` antes de chegar ao app.
- Atualizações automáticas do WordPress ficam desligadas (`AUTOMATIC_UPDATER_DISABLED`): **atualizar é trocar a imagem do template** (veja abaixo).

## 1. Ver o template e seus parâmetros

```bash
curl -sS "$NEBULAR_API/v1/templates" \
  -H "Authorization: Bearer $NEBULAR_TOKEN"
```
```json
{"items": [{"id": "wordpress", "name": "WordPress", "version": 1}], "next_cursor": null, "has_more": false}
```

Cada item traz `params` (nome, tipo e padrão). O template declara o parâmetro `table_prefix` (texto, padrão `"wp_"`): o prefixo das tabelas do WordPress, até 16 caracteres, no formato `^[a-z][a-z0-9_]{0,14}_$`.

| Resposta | O que fazer |
|---|---|
| `401` / `403` | Token inválido ou sem escopo: pare e peça outro. |
| `429` | Espere `Retry-After`. |

## 2. Instanciar

Crie o projeto e instancie o template nele. A instanciação é **atômica** (se qualquer parte falhar, tudo que foi criado é desfeito) e **idempotente** pela `Idempotency-Key`: repetir a mesma chamada depois de timeout devolve o mesmo resultado e não duplica serviços.

```bash
NAME="blog-$(date +%s)-$RANDOM"
H="Authorization: Bearer $NEBULAR_TOKEN"
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \
  -d "{\"name\": \"$NAME\"}" | jq -r .id)

# o `web` que o projeto já traz, se virgem, é adotado pelo template (não apague)
curl -sS -X POST "$NEBULAR_API/v1/projects/$P/templates/wordpress" \
  -H "$H" -H "Content-Type: application/json" \
  -H "Idempotency-Key: $NAME-wordpress" \
  -d '{"params": {"table_prefix": "wp_"}}'
```
```json
{"template_id": "wordpress", "version": 1, "project_id": "01j9zkk07fhzv5zsmn996cdwpb", "services": [{"id": "01j9zkff8y1dmcwa49fanx9qak", "name": "db", "kind": "database"}, {"id": "01j9zkwf4q15w38y0taf680jvv", "name": "web", "kind": "app"}], "deploy_started": true}
```

| Resposta | O que fazer |
|---|---|
| `404` | Projeto ou template inexistente (ou projeto de outro time). |
| `400 invalid_request` | `params` inválido (tipo ou parâmetro desconhecido). |
| `409 name_taken` | O projeto já tem um serviço `db`, ou o `web` já foi usado (tem deploy, variáveis, domínio ou volume). Use um projeto novo, ou limpe o `web`. Um `web` virgem **não** dá erro: é adotado. |
| `422 quota_exceeded` | Limite do plano (serviços, memória, volume ou cota de banco). Veja [Limites e cotas](/operacao/limites-e-cotas/). |
| `503 unavailable` | O nó de banco está indisponível: nada foi criado (atômico); repita a **mesma** chamada com backoff de 5, 10, 20, 40 s. |

## 3. Acompanhar os deploys

Liste os serviços do projeto, pegue o deployment mais novo do `web` e faça polling dele por id. O `web` só fica `ready` depois que o banco `db` está provisionado (ele depende do `db` pelas referências); se o `db` falhar, o `web` termina `blocked`.

```bash
NAME="blog-$(date +%s)-$RANDOM"
H="Authorization: Bearer $NEBULAR_TOKEN"
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \
  -d "{\"name\": \"$NAME\"}" | jq -r .id)

# o `web` que o projeto já traz, se virgem, é adotado pelo template (não apague)
curl -sS -X POST "$NEBULAR_API/v1/projects/$P/templates/wordpress" \
  -H "$H" -H "Content-Type: application/json" \
  -H "Idempotency-Key: $NAME-wordpress" \
  -d '{"params": {"table_prefix": "wp_"}}' >/dev/null

S=$(curl -sS "$NEBULAR_API/v1/projects/$P/services" -H "$H" | jq -r '.items[] | select(.name=="web") | .id')

# o deploy do template é o mais novo da lista; acompanhe-o pelo id
D=$(curl -sS "$NEBULAR_API/v1/projects/$P/services/$S/deployments" -H "$H" | jq -r '.items[0].id')

delay=2
for i in $(seq 1 40); do
  st=$(curl -sS "$NEBULAR_API/v1/projects/$P/services/$S/deployments/$D" -H "$H" | jq -r '.status')
  echo "tentativa $i: $st"
  case "$st" in
    ready) break ;;
    failed|blocked|canceled|superseded) echo "deploy terminou em $st" >&2; exit 1 ;;
  esac
  sleep "$delay"; delay=$(( delay * 2 > 30 ? 30 : delay * 2 ))
done
[ "$st" = ready ]

# a URL pública vem em public_url
curl -sS "$NEBULAR_API/v1/projects/$P/services/$S" -H "$H" | jq -e '.public_url | startswith("https://")'  >/dev/null
```

Para ver o `409` quando o `web` já foi usado:

```bash
NAME="blog-$(date +%s)-$RANDOM"
H="Authorization: Bearer $NEBULAR_TOKEN"
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \
  -d "{\"name\": \"$NAME\"}" | jq -r .id)

# usa o web (variável) e então tenta o template
curl -sS -X PUT "$NEBULAR_API/v1/projects/$P/services/web/variables/FOO" -H "$H" -H "Content-Type: application/json" \
  -d '{"value": "bar"}' >/dev/null

curl -sS -X POST "$NEBULAR_API/v1/projects/$P/templates/wordpress" \
  -H "$H" -H "Content-Type: application/json" -d '{"params": {}}'
```

Abra `$URL/wp-admin/install.php` no navegador (ou complete a instalação do WordPress por uma pessoa): criar o usuário administrador é um passo humano.

| Estado ou erro | O que fazer |
|---|---|
| `blocked` no `web` | O `db` falhou: leia os logs de build/provisionamento do `db` e corrija. |
| `422 unresolved_reference` ao disparar o deploy | Uma variável `${{db.…}}` não existe: o serviço `db` foi apagado ou renomeado. |
| 502 / health check falhando | Memória insuficiente (suba `resources.memory_mb` para 512 ou mais, `resources.cpus` para 1) ou banco ainda em `provisioning`. Leia os logs de execução do `web`. |
| `503 unavailable` | Banco indisponível: repita com backoff; não recrie. |

## Domínio, backups e atualização

- URL gerada: [Subdomínio gerado](/dominios/subdominio-gerado/); domínio próprio: [Domínio próprio](/dominios/dominio-proprio/).
- O banco tem backup diário (7 diários e 4 semanais) e sob demanda: [Backup e restore](/bancos/backup-restore/). `wp-content` (volume) é separado: veja [Volumes](/conectados/volumes/).
- **Atualizar o WordPress** = trocar `source.ref` (nova tag e novo `@sha256:...`) do serviço `web` por uma versão mais nova da imagem oficial e fazer um novo deploy; o rollback volta à versão anterior. Faça backup do banco antes.

```bash
NAME="blog-$(date +%s)-$RANDOM"
H="Authorization: Bearer $NEBULAR_TOKEN"
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \
  -d "{\"name\": \"$NAME\"}" | jq -r .id)

# o `web` que o projeto já traz, se virgem, é adotado pelo template (não apague)
curl -sS -X POST "$NEBULAR_API/v1/projects/$P/templates/wordpress" \
  -H "$H" -H "Content-Type: application/json" \
  -H "Idempotency-Key: $NAME-wordpress" \
  -d '{"params": {"table_prefix": "wp_"}}' >/dev/null
S=$(curl -sS "$NEBULAR_API/v1/projects/$P/services" -H "$H" | jq -r '.items[] | select(.name=="web") | .id')

curl -sS -X PATCH "$NEBULAR_API/v1/projects/$P/services/$S" \
  -H "$H" -H "Content-Type: application/json" \
  -d '{"source": {"type": "image", "ref": "wordpress:6.9.5-php8.3-apache"}}'
curl -sS -X POST "$NEBULAR_API/v1/projects/$P/services/$S/deployments" \
  -H "$H" -H "Content-Type: application/json" -H "Idempotency-Key: wp-update-$NAME" -d '{}'
```
