Receba novidades do Hedhog no seu e-mail
Novos lançamentos, receitas e breaking changes — sem spam.
Submódulo cms
pages
CRUD de páginas, ciclo de vida de publicação/duplicação, snapshotting automático de revisões e gerenciamento por substituição total das instâncias de componente posicionadas nas regiões de layout de uma página.
Caminho de origem: libraries/cms/src/cms-page.controller.ts, cms-page.service.ts, cms-page-component.service.ts
Arquivo do módulo: cms.module.ts
Introdução
O submódulo Pages é dono de `cms_page`, o registro que o visitante do site acaba renderizando. Toda página tem um `type` (page, landing, blog_post, blog_index, custom) e um `status` (draft, review, scheduled, published, archived), e está vinculada a um único locale por meio de `locale_id` — o DTO aceita um *código* de locale (por exemplo, "en", "pt"), que o CmsPageService resolve para um ID por meio do LocaleService, lançando um 400 se o código for desconhecido. O mesmo padrão de resolução por código se aplica a `layout_slug` → `layout_id`: uma página não pode ser criada ou atualizada referenciando um layout que não existe. O slug é único por locale, garantido por um índice único parcial em `(slug, locale_id)` com escopo `WHERE deleted_at IS NULL` — assim, um slug liberado por uma página excluída logicamente (soft delete) pode ser reutilizado, mas duas páginas ativas no mesmo locale nunca podem colidir.
Toda ação de mutação em uma página — criar, atualizar, publicar, duplicar e restaurar — chama incondicionalmente um helper privado `createRevision()`, que faz um snapshot de todo o payload mapeado da página (não um diff) em `cms_page_revision`, marcado com um campo `action` e o usuário responsável. O número da versão é simplesmente a versão máxima anterior daquela página mais um. Isso significa que o histórico de revisões é uma trilha de auditoria completa de todos os estados pelos quais a página já passou, gerada automaticamente como efeito colateral da edição normal, e não por meio de qualquer endpoint explícito de "salvar revisão" — `GET /cms/pages/:id/revisions` as lista, da mais recente para a mais antiga. `DELETE /cms/pages/:id` é a única ação de mutação que **não** cria uma revisão: ela apenas define `deleted_at`, e tanto `list()` quanto `getById()` filtram por `deleted_at: null`, de modo que uma página excluída logicamente simplesmente desaparece de todos os endpoints de leitura, enquanto sua linha e seu histórico de revisões permanecem no banco de dados.
`POST /cms/pages/:id/publish` define `status: published`, carimba `published_at`/`published_by_id` e registra uma revisão `publish`. `POST /cms/pages/:id/duplicate` clona a página em um novo `draft`: acrescenta `-copy` ao slug e, se isso colidir dentro do mesmo `locale_id`, continua acrescentando `-1`, `-2`, … até encontrar um livre, mas o título clonado sempre recebe um sufixo fixo em português (`" (cópia)"`), independentemente do locale da própria página de origem — uma lacuna de localização importante de se conhecer caso a UI administrativa seja usada em outros idiomas. `POST /cms/pages/:id/revisions/:revisionId/restore` reaplica os campos de um snapshot passado (título, descrição, tipo, locale, layout, campos de SEO), mas **sempre força `status: draft`**, não importa qual status o snapshot tenha capturado — restaurar uma revisão que um dia foi `published` não republica a página; apenas prepara aquele conteúdo como um rascunho que precisa passar por `/publish` novamente de forma explícita.
`GET/POST /cms/pages/:pageId/components` e seus equivalentes `PATCH`/`DELETE` `:id` gerenciam `cms_page_component`, a junção entre uma página, uma das regiões de seu layout e um componente do catálogo. `POST .../components` (`saveComponents`) faz **substituição total, não incremental**: ele exclui todas as linhas `cms_page_component` existentes da página e recria toda a lista em massa a partir do array `instances` enviado, em uma única chamada. Um cliente que omitir uma instância existente do payload a perderá silenciosamente — aqui não há semântica de merge/upsert, diferentemente dos endpoints individuais `PATCH .../components/:id` e `DELETE .../components/:id`, que atuam sobre uma única instância pelo seu próprio ID. O `region_code` de cada instância é resolvido para um `layout_region_id` com escopo no `layout_id` da *própria página* (via `resolveRegionId(page.layout_id, region_code)`), de modo que um código de região que exista em um layout diferente é rejeitado com um 400, mesmo que a string do código seja válida em outro lugar; o `component_slug` é resolvido para `component_id` no catálogo global de componentes, sem esse escopo.
As tabelas de junção `cms_page_category` e `cms_page_tag` estão definidas em `libraries/cms/hedhog/table/*.yaml` (page ↔ `category`, page ↔ `tag`), mas **não têm controller, service ou endpoint correspondente** em nenhuma parte do módulo — o schema modela uma camada de taxonomia/tagging para páginas que a atual superfície de API não expõe de forma alguma. Não presuma que a atribuição de categoria ou tag seja acessível por meio deste ou de qualquer outro endpoint do CMS.
Endpoints HTTP
13 endpoints
Lista paginada de páginas, excluindo as que foram excluídas logicamente. A busca corresponde ao título ou ao slug (sem diferenciar maiúsculas de minúsculas). Os filtros layout e locale são resolvidos para seus IDs internos antes da consulta.
- Query
- page (default 1), pageSize (default 20), search?, status? (draft | review | scheduled | published | archived), type? (page | landing | blog_post | blog_index | custom), layout? (layout slug), locale? (locale code)
Cria uma nova página e registra sua revisão inicial (action: "create").
Body
| Field | Type | Required | Notes |
|---|---|---|---|
| slug | string | yes | — |
| title | string | yes | — |
| description | string | no | — |
| type | CmsPageType (page | landing | blog_post | blog_index | custom) | no | Default: page |
| status | CmsPageStatus (draft | review | scheduled | published | archived) | no | Default: draft |
| locale | string | yes | Locale code (e.g. "en"), resolved to locale_id |
| layout_slug | string | yes | Resolved to layout_id; 400 if the layout does not exist |
| seo_title | string | no | — |
| seo_description | string | no | — |
| author_id | number | no | Falls back to the authenticated user ID if omitted |
| scheduled_at | date string | no | — |
Obtém uma única página (não excluída) pelo ID.
- Params
- id (int)
Atualiza parcialmente uma página (mesmos campos da criação, todos opcionais). Registra uma nova revisão (action: "update"). Os editores podem atualizar páginas, mas não podem excluí-las.
- Params
- id (int)
Exclui logicamente (soft delete) uma página definindo deleted_at. Não registra uma revisão. Restrito aos papéis de admin — cms-editor não pode excluir páginas.
- Params
- id (int)
Publica uma página: define o status como published, carimba published_at/published_by_id e registra uma revisão (action: "publish").
- Params
- id (int)
Clona uma página em um novo rascunho com um sufixo de slug "-copy" (e, em caso de colisão, "-1", "-2", …) restrito ao mesmo locale, e um sufixo de título fixo " (cópia)". Registra uma revisão (action: "duplicate") referenciando o ID da página de origem.
- Params
- id (int)
Lista todas as revisões de uma página, da versão mais recente para a mais antiga.
- Params
- id (int)
Reaplica à página os campos do snapshot de uma revisão passada. Sempre redefine o status para draft, independentemente do status capturado no snapshot — um snapshot "published" restaurado precisa ser republicado explicitamente. Registra uma nova revisão (action: "restore") referenciando o ID da revisão de origem.
- Params
- id (int), revisionId (int)
Lista as instâncias de componente posicionadas em uma página, ordenadas por região de layout e, em seguida, por ordem.
- Params
- pageId (int)
Salvamento por substituição total das instâncias de componente de uma página: exclui todas as instâncias existentes da página e as recria em massa a partir do array enviado, em uma única chamada. Omitir uma instância existente a exclui — isto não é um upsert incremental. Cada item: id? (number, instância existente a manter), region_code (string, obrigatório, resolvido para layout_region_id com escopo no próprio layout da página), component_slug (string, obrigatório, resolvido para component_id), name?, props? (object), content? (object), order? (number), is_visible? (boolean), parent_id? (number, para instâncias aninhadas).
- Params
- pageId (int)
Body
| Field | Type | Required | Notes |
|---|---|---|---|
| instances | CmsPageComponentItemDto[] | yes | Full desired list of component instances for the page — see fields below |
Atualiza uma única instância de componente no local (diferentemente do endpoint de salvamento em massa, este atua sobre uma instância por ID).
- Params
- pageId (int), id (int) — component instance ID
Body
| Field | Type | Required | Notes |
|---|---|---|---|
| props | object | no | — |
| content | object | no | — |
| name | string | no | — |
| order | number | no | — |
| is_visible | boolean | no | — |
Remove uma única instância de componente de uma página.
- Params
- pageId (int), id (int) — component instance ID