@oondemand/oon-core-back 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE +91 -0
  2. package/README.md +130 -0
  3. package/bin/cli.js +95 -0
  4. package/bin/dev-runner.js +8 -0
  5. package/package.json +53 -0
  6. package/src/api/auth.routes.js +20 -0
  7. package/src/api/core.routes.js +75 -0
  8. package/src/api/status.routes.js +21 -0
  9. package/src/boot/activation.js +31 -0
  10. package/src/boot/createApp.js +56 -0
  11. package/src/boot/database.js +35 -0
  12. package/src/boot/loadCentral.js +51 -0
  13. package/src/boot/startServer.js +31 -0
  14. package/src/core/context.js +35 -0
  15. package/src/core/define.js +171 -0
  16. package/src/core/errors/GenericError.js +14 -0
  17. package/src/core/factories/crudController.js +66 -0
  18. package/src/core/factories/crudService.js +96 -0
  19. package/src/core/factories/modelRouter.js +67 -0
  20. package/src/core/fields.js +92 -0
  21. package/src/core/middlewares/auth.js +65 -0
  22. package/src/core/middlewares/correlationId.js +12 -0
  23. package/src/core/middlewares/error.js +46 -0
  24. package/src/core/middlewares/mutationAudit.js +40 -0
  25. package/src/core/middlewares/rbac.js +22 -0
  26. package/src/core/middlewares/requestLog.js +28 -0
  27. package/src/core/models/ControleAlteracao.js +28 -0
  28. package/src/core/models/Log.js +22 -0
  29. package/src/core/models/Sistema.js +27 -0
  30. package/src/core/registry.js +117 -0
  31. package/src/core/utils/helpers.js +44 -0
  32. package/src/deploy/render.js +88 -0
  33. package/src/deploy/templates/Dockerfile +19 -0
  34. package/src/deploy/templates/certificate.yaml +12 -0
  35. package/src/deploy/templates/configmap.yaml +16 -0
  36. package/src/deploy/templates/deployment.yaml +32 -0
  37. package/src/deploy/templates/hpa.yaml +19 -0
  38. package/src/deploy/templates/ingress.yaml +24 -0
  39. package/src/deploy/templates/secret.yaml +12 -0
  40. package/src/deploy/templates/service.yaml +14 -0
  41. package/src/index.js +43 -0
  42. package/src/modules/assistants/index.js +33 -0
  43. package/src/modules/currencies/index.js +31 -0
  44. package/src/modules/documents/index.js +19 -0
  45. package/src/modules/omie/index.js +69 -0
  46. package/src/modules/pipelines/index.js +19 -0
package/LICENSE ADDED
@@ -0,0 +1,91 @@
1
+ # LICENÇA DE USO RESTRITO DO CÓDIGO-FONTE OONDEMAND
2
+ **ODR‑NC v1.0 — Non‑Commercial, Internal‑Use Only**
3
+
4
+ Copyright (c) 2025 OonDemand. Todos os direitos reservados.
5
+
6
+ ## 1. Definições
7
+ **“Licenciante”**: OonDemand.
8
+ **“Código”**: todo o conteúdo deste repositório e seus derivados.
9
+ **“Licenciada”** ou **“Usuário Autorizado”**: a pessoa jurídica nominalmente autorizada pela Licenciante (via contrato ou arquivo `AUTORIZACOES.md`).
10
+ **“Uso Interno Não Comercial”**: uso exclusivo dentro da organização da Licenciada, para suas próprias operações internas, sem disponibilização a terceiros e sem geração de receita direta ou indireta a partir do Código, incluindo sem se limitar a: revenda, prestação de serviços a terceiros, franquia, SaaS, consultoria, sublicenciamento, distribuição ou incorporação em produtos/serviços comercializados.
11
+
12
+ ## 2. Concessão Limitada
13
+ Sujeito ao cumprimento integral desta Licença, a Licenciante concede à Licenciada uma licença **não exclusiva, intransferível e revogável** para **acessar, visualizar, modificar, compilar e executar** o Código **exclusivamente para Uso Interno Não Comercial**. Nenhum direito é concedido para **distribuir, publicar, sublicenciar, ceder** ou **oferecer a terceiros** o Código (ou obras derivadas).
14
+
15
+ Qualquer **Uso Comercial** (incluindo oferta como serviço, SaaS, revenda, consultoria, integração para clientes externos ou uso em qualquer outra empresa do mesmo grupo econômico) **exige autorização prévia e expressa da Licenciante, por escrito** e com condições comerciais específicas. Na ausência de tal autorização, o Uso Comercial é **estritamente proibido**.
16
+
17
+ ## 3. Restrições e Obrigações
18
+ A Licenciada se compromete a:
19
+ 1. **Não** remover ou alterar avisos de copyright, marcas, avisos de confidencialidade e desta Licença;
20
+ 2. **Não** publicar ou tornar o Código (ou parte significativa) acessível publicamente (incl. repositórios públicos, fóruns, pacotes);
21
+ 3. **Não** distribuir, vender, revender, licenciar, sublicenciar, alugar, arrendar, franquear, ceder ou transferir o Código a terceiros;
22
+ 4. **Não** utilizar o Código para treinar modelos de IA, realizar engenharia reversa de mecanismos de licenciamento ou produzir obra concorrente direta;
23
+ 5. Adotar controles de acesso (RBAC), registro de auditoria e medidas razoáveis de segurança para impedir uso não autorizado; e
24
+ 6. Garantir que terceiros (funcionários, prestadores) observem as mesmas obrigações de confidencialidade e uso restrito.
25
+
26
+ ## 4. Propriedade Intelectual
27
+ O Código é protegido por leis de propriedade intelectual. **A titularidade integral permanece com a Licenciante**. Exceto pelos direitos limitados concedidos nesta Licença, **nenhum outro direito** (explícito, implícito, por estoppel) é concedido.
28
+
29
+ ## 5. Autorização Expressa para Uso Comercial
30
+ Qualquer permissão de Uso Comercial deverá:
31
+ - Ser **prévia e por escrito**, concedida pela Licenciante;
32
+ - Constar em contrato específico **ou** no arquivo `AUTORIZACOES.md` deste repositório, com **razão social/CNPJ**, **escopo**, **prazo** e **condições comerciais**; e
33
+ - Ser **revogável** por descumprimento.
34
+
35
+ ## 6. Cláusula Penal (Multa) por Uso Indevido
36
+ O uso em desacordo com esta Licença (incluindo Uso Comercial sem autorização) sujeitará a Licenciada ao pagamento de **cláusula penal não compensatória** no valor de **R$ 600.000,00 (seiscentos mil reais) por infração**, **cumulável** com:
37
+ - **R$ 5.000,00 (cinco mil reais) por dia** enquanto perdurar a infração; e
38
+ - Indenização adicional por **perdas e danos** e **honorários advocatícios**.
39
+ A aplicação desta cláusula penal **não limita** o direito da Licenciante de buscar tutela específica, inclusive **medidas liminares/inibitórias**.
40
+
41
+ ## 7. Confidencialidade
42
+ O Código e a documentação correlata constituem **informação confidencial**. A Licenciada deve preservá-la com o mesmo grau de zelo usado para suas informações confidenciais, nunca inferior ao razoável.
43
+
44
+ ## 8. Ausência de Garantias; Limitação de Responsabilidade
45
+ O Código é fornecido “**no estado em que se encontra**”, sem garantias de qualquer tipo. Na máxima extensão permitida em lei, a Licenciante **não responde** por danos indiretos, lucros cessantes, perda de dados ou danos especiais. A responsabilidade total da Licenciante, por qualquer teoria, **fica limitada ao valor total efetivamente pago** pela Licenciada em favor da Licenciante pelo direito de uso do Código, se houver.
46
+
47
+ ## 9. Vigência, Revogação e Auditoria
48
+ Esta Licença vigora enquanto o Código estiver em posse da Licenciada e poderá ser **revogada** a qualquer tempo em caso de violação. A Licenciante poderá solicitar **evidências razoáveis de conformidade** (ex.: lista de usuários, ambientes de execução) mediante aviso prévio escrito de 5 dias úteis.
49
+
50
+ ## 10. Terceiros e Dependências
51
+ Componentes de terceiros incluídos no repositório permanecem regidos por suas **próprias licenças**. Em caso de conflito, prevalece a licença do respectivo terceiro para tal componente.
52
+
53
+ ## 11. Lei Aplicável e Foro
54
+ Aplica-se a legislação brasileira. Fica eleito o **Foro da Comarca de São Paulo/SP**, com renúncia a qualquer outro, por mais privilegiado que seja.
55
+
56
+ ## 12. Disposições Finais
57
+ Se qualquer cláusula for considerada inválida, as demais permanecem válidas. Qualquer tolerância não implicará renúncia. Esta é a versão **ODR‑NC v1.0**. A Licenciante poderá publicar **atualizações** desta Licença; versões futuras não terão efeito retroativo sem aceite da Licenciada.
58
+
59
+ ---
60
+
61
+ ### Identificação de Autorização (opcional, para uso interno)
62
+ > Este repositório está licenciado para uso interno por: **[RAZÃO SOCIAL]**, **CNPJ [XX.XXX.XXX/0001-XX]**, conforme autorização datada de **[DATA]** e/ou contrato específico. Escopo: **[DESCREVER]**.
63
+
64
+ ### Como cumprir esta Licença
65
+ 1) Mantenha este arquivo `LICENSE.md` na raiz do repositório;
66
+ 2) Liste autorizações de uso comercial, se houver, em `AUTORIZACOES.md`; e
67
+ 3) Inclua o cabeçalho de licença abaixo em **cada arquivo-fonte**.
68
+
69
+ ---
70
+
71
+ ## Cabeçalho de Licença para Arquivos-Fonte
72
+ Copie e adapte o bloco conforme a linguagem:
73
+
74
+ ### Padrão (C/Java/JS/TS)
75
+ /*
76
+ OonDemand — Código-Fonte Proprietário
77
+ Licença ODR‑NC v1.0 — Uso interno não comercial. Proibida revenda/redistribuição/uso comercial sem autorização expressa da OonDemand. Violação sujeita à multa, conforme LICENSE.md.
78
+ (c) 2025 OonDemand. Todos os direitos reservados.
79
+ */
80
+
81
+ ### Python
82
+ """
83
+ OonDemand — Código-Fonte Proprietário
84
+ Licença ODR‑NC v1.0 — Uso interno não comercial. Proibida revenda/redistribuição/uso comercial sem autorização expressa da OonDemand. Violação sujeita à multa, conforme LICENSE.md.
85
+ (c) 2025 OonDemand. Todos os direitos reservados.
86
+ """
87
+
88
+ ### Shell/YAML
89
+ # OonDemand — Código-Fonte Proprietário
90
+ # Licença ODR‑NC v1.0 — Uso interno não comercial. Proibida revenda/redistribuição/uso comercial sem autorização expressa da OonDemand. Violação sujeita à multa, conforme LICENSE.md.
91
+ # (c) 2025 OonDemand. Todos os direitos reservados.
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # @oondemand/oon-core-back
2
+
3
+ Core runtime para **Centrais Oon**. O backend de uma Central passa a ser
4
+ apenas domínio: você escreve `central.config.js` + `src/models` (e, quando
5
+ preciso, `validations / triggers / pipelines / documents / integrations /
6
+ routes`) e sobe tudo com `oonCore-back start`.
7
+
8
+ Toda a infraestrutura — boot, banco, auth, RBAC, logs, auditoria, paginação,
9
+ metadata, CRUD, módulos opinativos (Omie, Assistentes, Multi-moedas, Esteiras,
10
+ Documentos) e deploy (Docker + k8s) — vive aqui dentro.
11
+
12
+ ## Instalação
13
+
14
+ ```bash
15
+ npm install @oondemand/oon-core-back
16
+ ```
17
+
18
+ ## Uso mínimo
19
+
20
+ `central.config.js`:
21
+
22
+ ```js
23
+ module.exports = {
24
+ serviceName: "minha-central",
25
+ // opcional: provedor de auth (default = Meus Apps)
26
+ // auth: { verifyToken: async (token) => ({ tipo, nome, email }) },
27
+ };
28
+ ```
29
+
30
+ `src/models/Produto.js`:
31
+
32
+ ```js
33
+ const { defineModel, fields } = require("@oondemand/oon-core-back");
34
+
35
+ defineModel({
36
+ name: "Produto",
37
+ schema: {
38
+ nome: fields.string({ required: true }),
39
+ preco: fields.currency(),
40
+ moeda: fields.currencyCode(),
41
+ },
42
+ crud: { enabled: true, roles: { write: ["admin"] } },
43
+ });
44
+ ```
45
+
46
+ Subir:
47
+
48
+ ```bash
49
+ npx oonCore-back start # produção
50
+ npx oonCore-back dev # watch/reload
51
+ ```
52
+
53
+ Isso já expõe, para `Produto`:
54
+
55
+ ```
56
+ GET /produtos (list + paginação/filtros/busca/ordenação)
57
+ GET /produtos/:id
58
+ POST /produtos
59
+ PUT /produtos/:id
60
+ PATCH /produtos/:id
61
+ DELETE /produtos/:id
62
+ POST /produtos/import
63
+ GET /produtos/export
64
+ ```
65
+
66
+ Todas privadas (auth obrigatório), com RBAC e auditoria nas mutações.
67
+
68
+ ## API pública
69
+
70
+ ```js
71
+ const {
72
+ start, createApp, activate,
73
+ defineCentral, defineModel, fields,
74
+ defineCollection, defineDocument, definePipeline,
75
+ defineOmieMapping, defineRoutes, defineValidation, defineTrigger,
76
+ omie, GenericError, registry,
77
+ } = require("@oondemand/oon-core-back");
78
+ ```
79
+
80
+ ### `defineRoutes` — rotas customizadas com o contrato de segurança
81
+
82
+ Nunca se usa `express.Router()` cru. `router.private.*` aplica auth + RBAC
83
+ (+ auditoria opcional); `router.public.*` é aberto.
84
+
85
+ ```js
86
+ const { defineRoutes } = require("@oondemand/oon-core-back");
87
+
88
+ defineRoutes("/relatorios", (router) => {
89
+ router.private.get("/resumo", { roles: ["admin"] }, async (req, res) => {
90
+ res.json({ ok: true, usuario: req.usuario });
91
+ });
92
+ router.public.get("/health", async (_req, res) => res.json({ up: true }));
93
+ });
94
+ ```
95
+
96
+ ## Rotas `/core/*` (contrato com o oonCore-front)
97
+
98
+ ```
99
+ GET /core/metadata GET /core/models GET /core/models/:name
100
+ GET /core/collections GET /core/documents GET /core/pipelines
101
+ GET /core/integrations GET /core/permissions GET /core/actions
102
+ GET /core/menus GET /core/features
103
+ ```
104
+
105
+ ## CLI
106
+
107
+ ```bash
108
+ oonCore-back start # produção
109
+ oonCore-back dev # watch/reload
110
+ oonCore-back activate # seed/ativação (singleton Sistema + hook)
111
+ oonCore-back build:image # renderiza Dockerfile + docker build
112
+ oonCore-back k8s:render # renderiza manifests a partir de central.manifest.json
113
+ oonCore-back deploy # kubectl apply dos manifests
114
+ ```
115
+
116
+ O consumidor não escreve YAML: ajusta `central.manifest.json` e o Core
117
+ renderiza Dockerfile + ConfigMap/Secret/Deployment/Service/Ingress/HPA/Certificate.
118
+
119
+ ## Testes
120
+
121
+ ```bash
122
+ npm test # invariantes de segurança (offline)
123
+ MONGO_URI=... npm test # inclui CRUD + auditoria (requer Mongo)
124
+ ```
125
+
126
+ ## Variáveis de ambiente principais
127
+
128
+ `SERVICE_NAME`, `SERVICE_VERSION`, `PORT`, `NODE_ENV`,
129
+ `DB_SERVER`/`DB_NAME`/`DB_USER`/`DB_PASSWORD`/`DB_AUTH_SOURCE`/`DB_REPLICA_SET`/`DB_TSL`
130
+ (ou `MONGO_URI`), `MEUS_APPS_BACKEND_URL`, `OMIE_APP_KEY`/`OMIE_APP_SECRET`.
package/bin/cli.js ADDED
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const path = require("node:path");
5
+ const { spawnSync } = require("node:child_process");
6
+
7
+ const cmd = process.argv[2];
8
+ const cwd = process.cwd();
9
+
10
+ function loadManifest() {
11
+ return require("../src/deploy/render").loadManifest(cwd);
12
+ }
13
+
14
+ function run(bin, args) {
15
+ const r = spawnSync(bin, args, { stdio: "inherit", cwd });
16
+ if (r.error) {
17
+ console.error(`Falha ao executar ${bin}:`, r.error.message);
18
+ process.exit(1);
19
+ }
20
+ process.exit(r.status || 0);
21
+ }
22
+
23
+ async function main() {
24
+ switch (cmd) {
25
+ case "start": {
26
+ const { start } = require("../src");
27
+ await start({ cwd });
28
+ break;
29
+ }
30
+
31
+ case "dev": {
32
+ // Watch + restart usando o próprio Node (>=18.11 tem --watch).
33
+ process.env.NODE_ENV = process.env.NODE_ENV || "development";
34
+ const bootstrap = path.join(__dirname, "dev-runner.js");
35
+ run(process.execPath, ["--watch", bootstrap]);
36
+ break;
37
+ }
38
+
39
+ case "activate": {
40
+ const { activate } = require("../src");
41
+ await activate({ cwd });
42
+ process.exit(0);
43
+ break;
44
+ }
45
+
46
+ case "build:image": {
47
+ const m = loadManifest();
48
+ require("../src/deploy/render").renderDockerfile({ cwd });
49
+ const tag = `${m.IMAGE}:${m.IMAGE_TAG}`;
50
+ console.log(`Construindo imagem ${tag}...`);
51
+ run("docker", [
52
+ "build",
53
+ "--build-arg",
54
+ `SERVICE_VERSION=${m.IMAGE_TAG}`,
55
+ "-t",
56
+ tag,
57
+ ".",
58
+ ]);
59
+ break;
60
+ }
61
+
62
+ case "k8s:render": {
63
+ const files = require("../src/deploy/render").renderK8s({ cwd });
64
+ console.log("Manifests gerados:");
65
+ files.forEach((f) => console.log(" -", path.relative(cwd, f)));
66
+ process.exit(0);
67
+ break;
68
+ }
69
+
70
+ case "deploy": {
71
+ const files = require("../src/deploy/render").renderK8s({ cwd });
72
+ console.log("Aplicando manifests no cluster...");
73
+ run("kubectl", ["apply", ...files.flatMap((f) => ["-f", f])]);
74
+ break;
75
+ }
76
+
77
+ default:
78
+ console.log(`oonCore-back <comando>
79
+
80
+ Comandos:
81
+ start Sobe a Central (produção).
82
+ dev Sobe com watch/reload.
83
+ activate Executa seed/ativação (singleton Sistema + hook).
84
+ build:image Renderiza o Dockerfile e roda 'docker build'.
85
+ k8s:render Renderiza os manifests k8s a partir de central.manifest.json.
86
+ deploy Renderiza e aplica os manifests com 'kubectl apply'.
87
+ `);
88
+ process.exit(cmd ? 1 : 0);
89
+ }
90
+ }
91
+
92
+ main().catch((err) => {
93
+ console.error(err);
94
+ process.exit(1);
95
+ });
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ // Bootstrap usado por `oonCore-back dev` sob `node --watch`.
3
+ const { start } = require("../src");
4
+
5
+ start({ cwd: process.cwd() }).catch((err) => {
6
+ console.error(err);
7
+ process.exit(1);
8
+ });
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@oondemand/oon-core-back",
3
+ "version": "0.1.0",
4
+ "description": "Core runtime para Centrais Oon: Model Registry, CRUD/RBAC automáticos, módulos opinativos (Omie, Assistentes, Multi-moedas, Esteiras, Documentos) e deploy embutido.",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "oonCore-back": "bin/cli.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "src",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "test": "node --test"
16
+ },
17
+ "engines": {
18
+ "node": ">=18"
19
+ },
20
+ "keywords": [
21
+ "oondemand",
22
+ "ooncore",
23
+ "central",
24
+ "model-registry",
25
+ "express",
26
+ "mongoose"
27
+ ],
28
+ "license": "SEE LICENSE IN LICENSE",
29
+ "publishConfig": { "access": "public" },
30
+ "exports": {
31
+ ".": "./src/index.js"
32
+ },
33
+ "dependencies": {
34
+ "axios": "^1.7.7",
35
+ "cors": "^2.8.5",
36
+ "dotenv": "^16.4.5",
37
+ "express": "^4.21.2",
38
+ "helmet": "^8.0.0",
39
+ "mongoose": "^8.5.4",
40
+ "morgan": "^1.10.0"
41
+ },
42
+ "peerDependencies": {
43
+ "xlsx": "^0.18.5"
44
+ },
45
+ "peerDependenciesMeta": {
46
+ "xlsx": {
47
+ "optional": true
48
+ }
49
+ },
50
+ "devDependencies": {
51
+ "mongodb-memory-server": "^11.2.0"
52
+ }
53
+ }
@@ -0,0 +1,20 @@
1
+ const express = require("express");
2
+ const auth = require("../core/middlewares/auth");
3
+ const { asyncHandler, sendResponse } = require("../core/utils/helpers");
4
+
5
+ /**
6
+ * Rotas de autenticação do Core. O login propriamente dito é delegado ao
7
+ * provedor configurado (Meus Apps por padrão); aqui validamos o token e
8
+ * devolvemos o usuário resolvido.
9
+ */
10
+ const router = express.Router();
11
+
12
+ router.get(
13
+ "/validar-token",
14
+ auth,
15
+ asyncHandler(async (req, res) => {
16
+ sendResponse({ res, statusCode: 200, usuario: req.usuario });
17
+ })
18
+ );
19
+
20
+ module.exports = router;
@@ -0,0 +1,75 @@
1
+ const express = require("express");
2
+ const registry = require("../core/registry");
3
+ const ctx = require("../core/context");
4
+
5
+ /**
6
+ * Rotas /core/* — contrato de metadata com o oonCore-front (Seção 2.5/21).
7
+ * Expõem o que o registry conhece, sem domínio hardcoded.
8
+ */
9
+ const router = express.Router();
10
+
11
+ function modelSummary(entry) {
12
+ return {
13
+ name: entry.name,
14
+ singular: entry.singular,
15
+ // basePath em que o CRUD está montado — consumido pelo oonCore-front para
16
+ // resolver a URL do recurso no modo "dynamic" (default = /<singular>s).
17
+ basePath: entry.definition.basePath || `/${entry.singular}s`,
18
+ crud: entry.definition.crud?.enabled !== false,
19
+ roles: entry.definition.crud?.roles || {},
20
+ fields: Object.values(entry.metaFields),
21
+ searchable: entry.searchableFields,
22
+ };
23
+ }
24
+
25
+ router.get("/metadata", (_req, res) => {
26
+ res.json({
27
+ service: { name: ctx.config.serviceName, version: ctx.config.serviceVersion },
28
+ models: registry.listModels().map(modelSummary),
29
+ collections: registry.listCollections(),
30
+ documents: registry.listDocuments(),
31
+ pipelines: registry.listPipelines(),
32
+ integrations: registry.listIntegrations(),
33
+ features: registry.listFeatures(),
34
+ menus: registry.listMenus(),
35
+ });
36
+ });
37
+
38
+ router.get("/models", (_req, res) => {
39
+ res.json({ results: registry.listModels().map(modelSummary) });
40
+ });
41
+
42
+ router.get("/models/:name", (req, res) => {
43
+ const entry = registry.getModel(req.params.name);
44
+ if (!entry) return res.status(404).json({ message: "Model não registrada." });
45
+ res.json(modelSummary(entry));
46
+ });
47
+
48
+ router.get("/collections", (_req, res) => res.json({ results: registry.listCollections() }));
49
+ router.get("/documents", (_req, res) => res.json({ results: registry.listDocuments() }));
50
+ router.get("/pipelines", (_req, res) => res.json({ results: registry.listPipelines() }));
51
+ router.get("/integrations", (_req, res) => res.json({ results: registry.listIntegrations() }));
52
+
53
+ router.get("/permissions", (_req, res) => {
54
+ const permissions = registry.listModels().flatMap((entry) => {
55
+ const roles = entry.definition.crud?.roles || {};
56
+ return [
57
+ { resource: entry.name, action: "read", roles: roles.read || [] },
58
+ { resource: entry.name, action: "write", roles: roles.write || [] },
59
+ ];
60
+ });
61
+ res.json({ results: permissions });
62
+ });
63
+
64
+ router.get("/actions", (_req, res) => {
65
+ const actions = registry.listModels().map((entry) => ({
66
+ model: entry.name,
67
+ actions: ["list", "getById", "create", "update", "patch", "delete", "import", "export"],
68
+ }));
69
+ res.json({ results: actions });
70
+ });
71
+
72
+ router.get("/menus", (_req, res) => res.json({ results: registry.listMenus() }));
73
+ router.get("/features", (_req, res) => res.json({ results: registry.listFeatures() }));
74
+
75
+ module.exports = router;
@@ -0,0 +1,21 @@
1
+ const express = require("express");
2
+ const mongoose = require("mongoose");
3
+ const ctx = require("../core/context");
4
+
5
+ const router = express.Router();
6
+
7
+ router.get("/", (_req, res) => {
8
+ const states = {
9
+ 0: "Desconectado",
10
+ 1: "Conectado",
11
+ 2: "Conectando",
12
+ 3: "Desconectando",
13
+ };
14
+ const dbState = mongoose.connection.readyState;
15
+ res.status(200).json({
16
+ message: `${ctx.config.serviceName} rodando - vs ${ctx.config.serviceVersion}`,
17
+ database: states[dbState] || "Status desconhecido",
18
+ });
19
+ });
20
+
21
+ module.exports = router;
@@ -0,0 +1,31 @@
1
+ const ctx = require("../core/context");
2
+ const { connectDB } = require("./database");
3
+ const { loadCentral } = require("./loadCentral");
4
+ const Sistema = require("../core/models/Sistema");
5
+
6
+ /**
7
+ * Ativação/seed da Central. Substitui o antigo seedRouter + seeds/.
8
+ * Garante o singleton Sistema e executa o `activate` declarado em
9
+ * `central.config.js` (se houver), passando o contexto do Core.
10
+ */
11
+ async function activate({ cwd = process.cwd() } = {}) {
12
+ loadCentral(cwd);
13
+ await connectDB();
14
+
15
+ let sistema = await Sistema.findOne();
16
+ if (!sistema) {
17
+ sistema = await Sistema.create({ appKey: process.env.APP_KEY });
18
+ console.log("Sistema (singleton) criado.");
19
+ }
20
+
21
+ const hook = ctx.config.activate;
22
+ if (typeof hook === "function") {
23
+ await hook({ Sistema, sistema, registry: require("../core/registry") });
24
+ console.log("Hook de ativação da Central executado.");
25
+ }
26
+
27
+ console.log("Ativação concluída.");
28
+ return sistema;
29
+ }
30
+
31
+ module.exports = { activate };
@@ -0,0 +1,56 @@
1
+ const express = require("express");
2
+ const cors = require("cors");
3
+ const helmet = require("helmet");
4
+ const morgan = require("morgan");
5
+
6
+ const ctx = require("../core/context");
7
+ const registry = require("../core/registry");
8
+ const correlationId = require("../core/middlewares/correlationId");
9
+ const requestLog = require("../core/middlewares/requestLog");
10
+ const errorMiddleware = require("../core/middlewares/error");
11
+ const { buildModelRouter } = require("../core/factories/modelRouter");
12
+
13
+ const coreRoutes = require("../api/core.routes");
14
+ const statusRoutes = require("../api/status.routes");
15
+ const authRoutes = require("../api/auth.routes");
16
+
17
+ /**
18
+ * Cria a app Express do Core: middlewares transversais, rotas públicas,
19
+ * /core/*, routers de CRUD gerados pelo registry e rotas customizadas
20
+ * (defineRoutes). Tudo derivado do que foi registrado — sem domínio fixo.
21
+ */
22
+ function createApp() {
23
+ const app = express();
24
+
25
+ app.use(cors(ctx.config.cors));
26
+ app.use(helmet());
27
+ app.use(express.json({ limit: "10mb" }));
28
+ app.use(express.urlencoded({ extended: true, limit: "10mb" }));
29
+ app.use(correlationId);
30
+ if (process.env.NODE_ENV === "development") app.use(morgan("dev"));
31
+
32
+ // Públicas.
33
+ app.use("/", statusRoutes);
34
+ app.use("/auth", authRoutes);
35
+ app.use("/core", coreRoutes);
36
+
37
+ // Log de mutações (após status/core que são GET, antes do domínio).
38
+ app.use(requestLog);
39
+
40
+ // CRUD gerado por model registrada.
41
+ for (const entry of registry.listModels()) {
42
+ if (entry.definition.crud?.enabled === false) continue;
43
+ const basePath = entry.definition.basePath || `/${entry.singular}s`;
44
+ app.use(basePath, buildModelRouter(entry));
45
+ }
46
+
47
+ // Rotas customizadas (defineRoutes).
48
+ for (const { basePath, expressRouter } of registry.listRoutes()) {
49
+ app.use(basePath, expressRouter);
50
+ }
51
+
52
+ app.use(errorMiddleware);
53
+ return app;
54
+ }
55
+
56
+ module.exports = { createApp };
@@ -0,0 +1,35 @@
1
+ const mongoose = require("mongoose");
2
+
3
+ /**
4
+ * Conexão com o MongoDB a partir das envs padrão da Central.
5
+ * Extraído de `config/db.js`, sem acoplamento ao domínio.
6
+ */
7
+ function buildMongoUri() {
8
+ const cfg = {
9
+ dbServer: process.env.DB_SERVER,
10
+ dbName: process.env.DB_NAME,
11
+ dbAuthSource: process.env.DB_AUTH_SOURCE,
12
+ dbReplicaSet: process.env.DB_REPLICA_SET,
13
+ dbTsl: process.env.DB_TSL,
14
+ };
15
+
16
+ if (process.env.MONGO_URI) return process.env.MONGO_URI;
17
+
18
+ let uri = `${cfg.dbServer}/${cfg.dbName}?`;
19
+ if (cfg.dbAuthSource) uri += `authSource=${cfg.dbAuthSource}&`;
20
+ if (cfg.dbTsl) uri += `tls=true&`;
21
+ if (cfg.dbReplicaSet) uri += `replicaSet=${cfg.dbReplicaSet}`;
22
+ return uri;
23
+ }
24
+
25
+ async function connectDB() {
26
+ const uri = buildMongoUri();
27
+ await mongoose.connect(uri, {
28
+ user: process.env.DB_USER,
29
+ pass: process.env.DB_PASSWORD,
30
+ });
31
+ console.log(`Conectado ao MongoDB (${process.env.DB_NAME || uri})`);
32
+ return mongoose.connection;
33
+ }
34
+
35
+ module.exports = { connectDB, buildMongoUri };
@@ -0,0 +1,51 @@
1
+ const fs = require("node:fs");
2
+ const path = require("node:path");
3
+ const ctx = require("../core/context");
4
+
5
+ /**
6
+ * Descobre e carrega a Central do consumidor:
7
+ * 1. `central.config.js` na raiz do projeto (mescla na config do Core);
8
+ * 2. arquivos de domínio em `src/{models,validations,triggers,routes,
9
+ * pipelines,documents,integrations,mappings}` (apenas `require`,
10
+ * o efeito colateral é registrar via `define*`).
11
+ */
12
+ const DOMAIN_DIRS = [
13
+ "models",
14
+ "validations",
15
+ "triggers",
16
+ "routes",
17
+ "pipelines",
18
+ "documents",
19
+ "integrations",
20
+ "mappings",
21
+ "hooks",
22
+ ];
23
+
24
+ function loadCentral(cwd = process.cwd()) {
25
+ const configPath = path.join(cwd, "central.config.js");
26
+ if (fs.existsSync(configPath)) {
27
+ const config = require(configPath);
28
+ ctx.setConfig(typeof config === "function" ? config() : config);
29
+ }
30
+
31
+ const srcDir = path.join(cwd, "src");
32
+ for (const dir of DOMAIN_DIRS) {
33
+ const full = path.join(srcDir, dir);
34
+ if (fs.existsSync(full)) requireDir(full);
35
+ }
36
+
37
+ return ctx.config;
38
+ }
39
+
40
+ function requireDir(dir) {
41
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
42
+ const full = path.join(dir, entry.name);
43
+ if (entry.isDirectory()) {
44
+ requireDir(full);
45
+ } else if (/\.(js|cjs)$/.test(entry.name)) {
46
+ require(full);
47
+ }
48
+ }
49
+ }
50
+
51
+ module.exports = { loadCentral, DOMAIN_DIRS };