Logotipo Hedhog

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

FieldTypeRequiredNotes
slugstringyes
titlestringyes
descriptionstringno
typeCmsPageType (page | landing | blog_post | blog_index | custom)noDefault: page
statusCmsPageStatus (draft | review | scheduled | published | archived)noDefault: draft
localestringyesLocale code (e.g. "en"), resolved to locale_id
layout_slugstringyesResolved to layout_id; 400 if the layout does not exist
seo_titlestringno
seo_descriptionstringno
author_idnumbernoFalls back to the authenticated user ID if omitted
scheduled_atdate stringno

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

FieldTypeRequiredNotes
instancesCmsPageComponentItemDto[]yesFull 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

FieldTypeRequiredNotes
propsobjectno
contentobjectno
namestringno
ordernumberno
is_visiblebooleanno

Remove uma única instância de componente de uma página.

Params
pageId (int), id (int) — component instance ID