Um recurso é qualquer registro do banco que nasce de arquivos versionados: um layout, uma página, um componente, uma variável de texto, um prompt de IA, uma tarefa agendada, um menu. Você edita arquivos; o pipeline os compila em *Data.json; o atualizador grava no banco.
O caminho de um recurso
resources/<idioma>/<tipo>/<id>/<id>.html (+ .css, .precompiled.css, .md)
resources/<idioma>/<tipo>.json metadados (nome, caminho, layout, versão, checksum)
│ c2f resources:sync (atualizacao-dados-recursos.php)
▼
gestor/db/data/<Tabela>Data.json + gestor/db/data/schema-metadata.json
│ atualizador (atualizacoes-banco-de-dados.php), no deploy
▼
tabelas paginas, layouts, componentes, templates, variaveis, …
│ c2f css:rebuild
▼
css_precompiled regenerado a partir do HTML do banco, com assinatura de procedênciaPara o sistema, o pipeline completo é c2f manager:update-all. Para um projeto, c2f project:update-all <id>. Os dois terminam com css:rebuild.
Onde os recursos vivem
| Origem | Pasta | Tipos |
|---|---|---|
| Globais do core ou do projeto | gestor/resources/<idioma>/ com os arquivos listados em resources.map.php |
layouts, pages, components, templates, variables |
| Módulos | gestor/modulos/<id>/resources/<idioma>/, declarados no bloco resources.<idioma> do <id>.json |
os mesmos e mais ai_prompts, ai_prompts_targets, ai_modes, widgets (só registro, sem arquivos) e forms |
| Tarefas agendadas | chave cron na raiz do <id>.json do módulo |
uma por id, sem idioma (biblioteca cron.php) |
| Tabelas declarativas | tables_config.json (core), project_tables_config.json (projeto) ou o bloco tabela do módulo, com sync_resources: true |
qualquer tabela: menus, publisher_pages, pages_index… |
Os idiomas vêm de resources.map.php (pt-br e en no core). Um recurso que só existe em um idioma simplesmente não existe no outro.
Anatomia
gestor/resources/pt-br/pages.json
gestor/resources/pt-br/pages/404-pagina-nao-encontrada/404-pagina-nao-encontrada.html{
"name": "404 - Página Não Encontrada",
"id": "404-pagina-nao-encontrada",
"layout": "layout-pagina-sem-permissao",
"path": "404/",
"type": "page",
"without_permission": true,
"version": "1.4",
"checksum": { "html": "ee70…", "css": "", "combined": "ee70…" }
}Os metadados usam nomes em inglês que o compilador traduz para as colunas: name → nome, layout → layout_id, path → caminho, type → tipo (page → pagina, system → sistema), module → modulo, option → opcao, root → raiz, without_permission → sem_permissao. As formas em português também são aceitas. Página sem path ganha <id>/.
Versão e checksum
-
Não edite
versionnemchecksumà mão. A etapa 2 doresources:syncrecalcula o checksum de cada recurso e, se mudou, incrementaversione reescreve o JSON de origem. Por isso umresources:synccostuma deixarpages.json,layouts.jsone os<modulo>.jsonalterados no Git: é esperado e deve ser commitado junto. -
O checksum é MD5 (
html,css,css_precompiledecombined); prompts usam o MD5 do.md. Não é uma verificação de segurança, só de mudança. -
A versão tem o formato
X.Ye só oYsobe (1.27→1.28). Um valor fora desse formato (2.0.1,v2) é reiniciado para1.0. -
No
*Data.jsonhá duas versões:file_version(a da origem) eversao, um inteiro que sobe quando o checksum difere do*Data.jsonanterior.
Unicidade e órfãos
O compilador rejeita duplicados e os grava em gestor/db/orphans/<Tipo>Data.json, fora do deploy:
| Tipo | Único por |
|---|---|
| Layouts, componentes | idioma + módulo + id |
| Páginas | idioma + módulo + id e idioma + caminho (sem barras, minúsculo), mesmo entre módulos |
| Templates | idioma + target + id |
| Variáveis | idioma + módulo + id + grupo. Sem grupo, só uma |
| Prompts, modos, alvos de IA, widgets | idioma + id |
| Tarefas de cron | id (também são rejeitadas sem callback ou com frequência inválida) |
O atualizador: o que é sobrescrito no deploy
O atualizador compara cada *Data.json com a tabela e só processa as tabelas cujo arquivo mudou desde a última execução. As regras de cada tabela vêm de schema-metadata.json, gerado pelo compilador a partir de tables_config.json, do bloco tabela dos módulos e do project_tables_config.json:
-
strategy:natural_key(casa pelas colunas denatural_key_columns, comolanguage, modulo, id) oupk. -
insert_only: só insere, nunca atualiza (é o caso deusuarios). Vale nas duas estratégias: é o que impede a semente de regravar login, e-mail e senha do administrador de uma instalação. -
preserve_on_user_modified: campos protegidos quando o registro temuser_modified=1, ou seja, foi editado pelo painel. Empaginas:nome, layout_id, caminho, framework_css, sem_permissao, html, css, css_compiled. -
deletareforcar_atualizacao: listas de registros a remover ou a sobrescrever ignorando as proteções (e voltandouser_modifieda 0).
Quando um campo protegido difere, o valor novo do sistema não se perde: vai para a coluna espelho (html_updated, css_updated; em variáveis, value_updated) e o registro recebe system_updated=1. Hoje nenhuma tela do painel lê essas colunas: a versão do sistema fica guardada, mas aplicá-la é manual (ou por forcar_atualizacao).
Sementes de sistema num projeto. As tabelas que o tables_config.json do core semeia (módulos, grupos, operações, perfis, permissões, usuários, categorias) são lidas, na compilação de um projeto, das sementes do projeto. Sem semente no projeto, a tabela é pulada (DYNAMIC_SKIP_PROJETO_SEM_SEMENTE no log): os dados do core chegam ao banco pela sincronização do core, não pelo db/data do projeto.
Escrita que falha interrompe. Se o compilador não consegue gravar um *Data.json (arquivo bloqueado, por exemplo), ele tenta de novo e, persistindo a falha, encerra com erro. Seguir em frente deixaria o banco com o conteúdo antigo, porque a versão e o checksum do recurso já teriam avançado nos arquivos de origem.
Projetos. No deploy de um projeto (--project=<id>), os registros tocados recebem project=<id>. Uma atualização posterior do core não sobrescreve registros de projeto, a não ser campos não protegidos de registros editados pelo usuário, e nunca troca o css_precompiled deles.
Opções do atualizador (php gestor/controladores/atualizacoes/atualizacoes-banco-de-dados.php): --dry-run, --log-diff, --debug, --force-all, --tables=paginas,variaveis, --skip-migrate, --backup, --reverse (exporta o banco para *Data.json), --orphans-mode=ignore|export|log, --hooks-only, --project=<id>, --env-dir=<dominio>.
CSS: autoria e derivado
Cada layout, página, componente e template tem quatro colunas de CSS:
| Coluna | Papel | Quem escreve |
|---|---|---|
css |
Autoria: o CSS escrito à mão (<id>.css) |
você |
css_precompiled |
Derivado: o Tailwind compilado para aquele HTML (<id>.precompiled.css) |
resources:sync (a partir dos arquivos) e css:rebuild (a partir do banco) |
css_compiled |
Derivado: o delta gerado pelo editor online | o editor visual |
css_source_hash |
Assinatura de procedência: de que HTML, CSS, layout e versão do Tailwind o derivado saiu | quem gera o derivado |
No HTML final, cada um entra num <style> marcado (data-c2f-css-role="authored", data-tailwind-role="page-precompiled", data-c2f-css-role="compiled"), deduplicado por hash.
Quando o deploy muda a autoria ou o derivado, o atualizador zera css_source_hash: o CSS atual continua servido, mas o recurso passa a contar como desatualizado, e o c2f css:rebuild o recompila contra o HTML que está no banco. É o que evita o estado híbrido "HTML novo com CSS antigo". Veja c2f css:rebuild --help para --project, --tipo, --id, --todos e --dry-run.
Tabelas declarativas (sync_resources)
Qualquer tabela pode virar recurso sem código novo. No project_tables_config.json do projeto (ou no bloco tabela.config de um módulo):
{
"tabelas": {
"menus": {
"nome": "menus",
"id": "id",
"id_numerico": "id_menus",
"config": {
"scope": "global",
"strategy": "natural_key",
"natural_key_columns": ["language", "id"],
"sync_resources": true,
"metadata_file": "menus.json",
"field_types": { "html": "file:html", "css": "file:css", "fields_schema": "json" }
}
}
}
}-
scope:global(arquivos emgestor/resources/) oumodule(arquivos emgestor/modulos/<modulo>/resources/, com"modulo": "<id>"; a colunamodulo/moduleé preenchida sozinha). -
metadata_file: JSON com a lista de registros, em<base>/<idioma>/<resources_dir ou tabela>/. Sem ele, os registros ficam inline emresources.<idioma>.<tabela>. -
field_types:jsonserializa o valor;file:<ext>injeta o conteúdo de<id>/<id>.<ext>. -
id: a coluna que identifica o registro (pode ser outra, comopage_id). -
Os registros ganham
languageestatus='A'quando ausentes. -
O compilador gera
<Tabela>Data.json(menus→MenusData.json).
Quando existe project_tables_config.json, o compilador também grava project-schema-metadata.json na raiz do Gestor do projeto. Ele vai no deploy para que o servidor, que não tem resources/, saiba quais tabelas pertencem ao projeto ao responder _api/project/recover (o pull do banco).
Data hooks. Um data-hooks.php em gestor/resources/, em gestor/db/ ou na pasta de um módulo é incluído no fim da compilação, para pós-processar os dados gerados.
Detalhes e armadilhas
-
c2f resources:sync --forceaparece na ajuda, mas não é repassado ao compilador: não tem efeito. Da mesma forma, as opções do script (como--no-origin-update) só funcionam chamandophp gestor/controladores/agents/arquitetura/atualizacao-dados-recursos.phpdireto. -
Depois de compilar, o
resources:syncpublica os assets empublic_html/dist/(assets:publish --opcional); semPUBLIC_PATH, só avisa. -
JavaScript, CSS e imagens estáticos (servidos por URL, fora de
resources/) têm token de cache automático: oresources:synccalcula o SHA-256 dos arquivos de cada módulo e gravaasset_versionno<modulo>.json, e faz o mesmo paragestor/assets/emassets/asset-versions.json. Não é preciso subir a versão do módulo à mão; basta commitar esses arquivos alterados. -
O runtime em modo de desenvolvimento lê do disco layouts, páginas e componentes, mas não templates, variáveis nem tabelas declarativas.
Veja também
-
Ciclo de uma requisição: onde o HTML do banco vira página.
-
Biblioteca gestor.php:
gestor_componente(),gestor_layout(),gestor_pagina_recursos_incluir(). -
Skills
c2f-resources-system,c2f-project-pipeline-and-tasksec2f-html-css-pages-and-components.