Pular para o conteúdo

Contrato de módulo

Um módulo do painel é um arquivo que exporta um manifesto criado por defineModule. Esse manifesto descreve o módulo: o identificador, as coleções, as telas, as rotas públicas, as tarefas, os ganchos e o que o módulo publica no site. Tudo o que o módulo pode importar do núcleo sai de um só lugar, panel/contract/index.ts. Um módulo não importa nada de panel/core/ nem dos adaptadores.

O exemplo abaixo é um módulo mínimo que usa os tipos principais: o manifesto (ModuleDef), o que o módulo publica no site (ModuleSite), o tipo dos serviços do painel (PanelServices), um gancho (HookHandler) e o tipo do conteúdo publicado (SiteSnapshot). A primeira linha diz em que lugar do projeto o arquivo vai. O restante é o código.

src/modules/recados/module.ts
import {
collection,
defineModule,
field,
type HookHandler,
type ModuleDef,
type ModuleSite,
type PanelServices,
type SiteSnapshot,
} from "../../panel/contract/index";
/** Publica no site a contagem de recados; o site só lê o que o módulo entrega em `content`. */
const site: ModuleSite = {
content: async (services: PanelServices): Promise<Partial<SiteSnapshot>> => {
const { items } = await services.store.list("recados", { perPage: 100 });
return { modules: { recados: { total: items.length } } };
},
};
/** Gancho depois de criar um recado: aqui ele só confirma que a escrita passou. */
const depoisDeCriar: HookHandler = async () => undefined;
/** Módulo mínimo com os tipos principais do contrato: manifesto, coleção, site e gancho. */
export const recados: ModuleDef = defineModule({
id: "recados",
version: "1.0.0",
core: "^1.0.0",
title: "Recados",
description: "Recados curtos do site.",
permissions: {
read: ["admin", "editor"],
write: ["admin", "editor"],
},
permissionLabels: {
read: "Ver recados",
write: "Criar e editar recados",
},
menu: [{ group: "organizacao", label: "Recados", collection: "recados", order: 40 }],
collections: {
recados: collection({
label: "Recados",
labelSingular: "Recado",
fields: {
titulo: field.text({ label: "Título", required: true }),
},
list: { columns: ["titulo"], search: ["titulo"], sort: { field: "titulo", direction: "asc" } },
permissions: { read: "read", create: "write", update: "write", delete: "write" },
}),
},
hooks: { "recados.afterCreate": depoisDeCriar },
site,
});
export { recados as module };

Para o painel carregar o módulo, ele precisa estar no índice src/modules/index.ts. O slash module add faz isso para os módulos oficiais. Para um módulo seu, acrescente o módulo ao índice e reinicie o servidor.

O arquivo panel/contract/index.ts reúne as exportações por assunto.

  • Manifesto: defineModule, com os tipos ModuleDef e ModuleInput. A função confere o manifesto inteiro e junta todos os erros em uma mensagem: identidade, dependências, permissões, rótulos de atividade, grupos de menu, coleções, telas, menu, rotas, tarefas, configurações, extensões, ganchos, site e migrações.
  • Coleções: collection e field, com os tipos CollectionDef, CollectionInput, Fields, FieldDef e FieldKind. Também uniqueConstraints, checkCollection, SYSTEM_FIELDS, COLLECTION_ACTIONS e GENERATED_SCREENS.
  • Telas e rotas: screen e route, com ScreenDef, ScreenResult, ScreenNotice, RouteDef, RouteAccess, RouteContext e HttpMethod.
  • Rotas públicas: publicGuard, readPublicBody, publicReply, PUBLIC_BODY_MAX_BYTES e HONEYPOT_FIELD, com PublicBody, PublicOutcome e PublicRefusal.
  • Tarefas: job e JOB_INTERVALS, com JobDef, JobContext e JobInterval.
  • Ganchos: os tipos HookKey, HookHandler, HookEvent, HookOutcome e DeleteReason. A chave tem a forma colecao.beforeAcao ou colecao.afterAcao, como posts.afterCreate.
  • Site: ModuleSite, com SiteBlockDef, SiteFormDef e SiteRedirect, e os tipos publicados (SiteSnapshot, PublishedPost, PublishedPage, PublishedCategory, PublishedTag, PublishedAuthor e outros).
  • Banco: MigrationDef e MigrationDb. Cada passo é ordenado e idempotente, e é desfeito por inteiro se falhar.
  • Serviços e contexto: PanelServices, RequestContext, ScreenContext, SessionUser, Role e ROLES. Os papéis são admin, editor e autor. Os serviços são as portas do núcleo: quem escreve o módulo recebe services.store, services.mailer e as outras (ver Portas).
  • Menu e permissões: MenuItem, MenuGroup, CORE_MENU_GROUPS, permissionKey, RESERVED_MODULE_IDS e RESERVED_COLLECTIONS.
  • Mídia, SEO e autores: libraryImage, ImageLibrary, publishedSeo, SEO_FIELDS, publishedAuthor e authorSlug.
  • Extensões de módulos existentes: extensionControls, extensionValues e mediaChoices.
  • Versões: satisfies, isRange e isVersion, usados para checar o campo core.
  • Utilidades: listAll, LIST_PAGE_SIZE, slugify, canonicalPath, isLocalPath e isInvited.
  • Erros: ManifestError e MESSAGES.
  • O id do módulo segue o padrão de minúsculas com hífen ([a-z][a-z0-9]*(-[a-z0-9]+)*). Ele também é o prefixo das permissões, das telas e da API do módulo.
  • Alguns ids e nomes de coleção são reservados ao núcleo, porque o núcleo usa as rotas /painel/<id>, /api/<id> e /painel/api/<id>. RESERVED_MODULE_IDS e RESERVED_COLLECTIONS listam quais.
  • O campo core é um intervalo de versões do núcleo, como ^1.0.0. O slash module add recusa o módulo quando a versão do núcleo do projeto não está no intervalo.

O kit de conformidade confere a estrutura do módulo, as coleções, as permissões e as telas. O tutorial de conformidade mostra como rodar o kit. Os limites de instalação e de atualização dos módulos estão em Limites da 1.1.0.