@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
@@ -0,0 +1,40 @@
1
+ const ControleAlteracao = require("../models/ControleAlteracao");
2
+
3
+ /**
4
+ * Trilha de auditoria de mutações. Em respostas de sucesso (status < 400)
5
+ * grava um registro em ControleAlteracao com o usuário, a entidade e o
6
+ * registro afetado. Usado pelas factories de CRUD e por `defineRoutes`.
7
+ *
8
+ * @param {{ entidade: string, acao: string }} opts
9
+ */
10
+ function mutationAudit({ entidade, acao }) {
11
+ return (req, res, next) => {
12
+ const origem = req.headers["x-origem"] || "api";
13
+
14
+ const originalJson = res.json.bind(res);
15
+ let responseBody;
16
+ res.json = (body) => {
17
+ responseBody = body;
18
+ return originalJson(body);
19
+ };
20
+
21
+ res.on("finish", () => {
22
+ if (res.statusCode >= 400) return;
23
+ const { message, ...rest } = responseBody || {};
24
+ const registro = rest[Object.keys(rest)[0]];
25
+
26
+ ControleAlteracao.create({
27
+ entidade,
28
+ acao,
29
+ origem,
30
+ usuario: { nome: req?.usuario?.nome, email: req?.usuario?.email },
31
+ idRegistro: registro?._id,
32
+ dadosAtualizados: registro,
33
+ }).catch((e) => console.log("ERRO AO REGISTRAR AÇÃO", e?.message));
34
+ });
35
+
36
+ next();
37
+ };
38
+ }
39
+
40
+ module.exports = { mutationAudit };
@@ -0,0 +1,22 @@
1
+ const Helpers = require("../utils/helpers");
2
+
3
+ /**
4
+ * RBAC simples baseado em `req.usuario.tipo`. `roles` vazio => qualquer
5
+ * usuário autenticado. `admin` sempre passa.
6
+ *
7
+ * @param {string[]} roles papéis permitidos
8
+ */
9
+ function rbac(roles = []) {
10
+ return (req, res, next) => {
11
+ if (!roles.length) return next();
12
+ const tipo = req.usuario?.tipo;
13
+ if (tipo === "admin" || roles.includes(tipo)) return next();
14
+ return Helpers.sendErrorResponse({
15
+ res,
16
+ statusCode: 403,
17
+ message: "Acesso negado para o seu perfil.",
18
+ });
19
+ };
20
+ }
21
+
22
+ module.exports = { rbac };
@@ -0,0 +1,28 @@
1
+ const Log = require("../models/Log");
2
+
3
+ /**
4
+ * Registra requisições mutantes (não-GET) na model interna Log.
5
+ * Captura status e corpo da resposta no `finish`.
6
+ */
7
+ const requestLog = async (req, res, next) => {
8
+ if (req.method === "GET") return next();
9
+
10
+ const log = new Log({
11
+ usuario: req.usuario || null,
12
+ endpoint: req.originalUrl,
13
+ metodo: req.method,
14
+ ip: req.ip || req.headers["x-forwarded-for"] || req.connection?.remoteAddress,
15
+ correlationId: req.correlationId,
16
+ dadosRequisicao: req.body,
17
+ });
18
+
19
+ res.on("finish", () => {
20
+ log.statusResposta = res.statusCode;
21
+ log.dadosResposta = res.locals.body || null;
22
+ log.save().catch(() => {});
23
+ });
24
+
25
+ next();
26
+ };
27
+
28
+ module.exports = requestLog;
@@ -0,0 +1,28 @@
1
+ const mongoose = require("mongoose");
2
+
3
+ /**
4
+ * Trilha de auditoria de mutações. Diferente da versão do CST, no Core
5
+ * `entidade` é uma string livre (o registry conhece o nome da model em
6
+ * runtime) e `acao`/`origem` aceitam os valores padrão sem enum rígido,
7
+ * porque o Core não pode enumerar o domínio do cliente.
8
+ */
9
+ const ACOES_PADRAO = ["adicionado", "alterado", "excluido"];
10
+ const ORIGENS_PADRAO = ["form", "datagrid", "importacao", "api"];
11
+
12
+ const controleAlteracaoSchema = new mongoose.Schema({
13
+ dataHora: { type: Date, default: Date.now, required: true },
14
+ usuario: { nome: String, email: String },
15
+ entidade: { type: String, required: true },
16
+ acao: { type: String, required: true },
17
+ origem: { type: String, default: "api" },
18
+ idRegistro: { type: String },
19
+ dadosAtualizados: { type: Object },
20
+ });
21
+
22
+ const ControleAlteracao =
23
+ mongoose.models.ControleAlteracao ||
24
+ mongoose.model("ControleAlteracao", controleAlteracaoSchema);
25
+
26
+ module.exports = ControleAlteracao;
27
+ module.exports.ACOES_PADRAO = ACOES_PADRAO;
28
+ module.exports.ORIGENS_PADRAO = ORIGENS_PADRAO;
@@ -0,0 +1,22 @@
1
+ const mongoose = require("mongoose");
2
+
3
+ /**
4
+ * Log de requisições mutantes. Model interna do Core — registrada
5
+ * automaticamente pelo requestLog middleware. Não é domínio do cliente.
6
+ */
7
+ const logSchema = new mongoose.Schema(
8
+ {
9
+ usuario: { type: Object, required: false },
10
+ endpoint: { type: String, required: true },
11
+ metodo: { type: String, required: true },
12
+ ip: { type: String },
13
+ correlationId: { type: String },
14
+ dadosRequisicao: { type: Object },
15
+ dadosResposta: { type: Object },
16
+ statusResposta: { type: Number },
17
+ },
18
+ { timestamps: true }
19
+ );
20
+
21
+ module.exports =
22
+ mongoose.models.Log || mongoose.model("Log", logSchema);
@@ -0,0 +1,27 @@
1
+ const mongoose = require("mongoose");
2
+
3
+ /**
4
+ * Configuração singleton da Central (chaves de integração, remetente, etc.).
5
+ * Model interna do Core. O cliente estende via `central.config.js`.
6
+ */
7
+ const omieSchema = new mongoose.Schema(
8
+ {
9
+ id_conta_corrente: { type: Number },
10
+ codigo_categoria: { type: String },
11
+ },
12
+ { _id: false }
13
+ );
14
+
15
+ const sistemaSchema = new mongoose.Schema(
16
+ {
17
+ omie: omieSchema,
18
+ openIaKey: String,
19
+ appKey: String,
20
+ sendgrid_api_key: { type: String },
21
+ remetente: { type: { nome: String, email: String } },
22
+ },
23
+ { timestamps: true }
24
+ );
25
+
26
+ module.exports =
27
+ mongoose.models.Sistema || mongoose.model("Sistema", sistemaSchema);
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Model Registry — coração técnico do Core (Seção 17).
3
+ *
4
+ * Tudo que o dev declara via `define*` é registrado aqui. A partir do
5
+ * registry, o Core deriva: schemas Mongoose, CRUD, RBAC, OpenAPI/metadata,
6
+ * rotas /core/* e os hooks dos módulos opinativos.
7
+ *
8
+ * É um singleton de processo. `reset()` existe para testes.
9
+ */
10
+ const store = {
11
+ models: new Map(), // name -> { name, mongooseModel, definition }
12
+ validations: new Map(), // modelName -> validateFn
13
+ triggers: new Map(), // modelName -> { before, after }
14
+ routes: [], // [{ basePath, build }]
15
+ collections: new Map(),
16
+ documents: new Map(),
17
+ pipelines: new Map(),
18
+ omieMappings: new Map(),
19
+ integrations: new Map(),
20
+ menus: [],
21
+ features: new Map(),
22
+ };
23
+
24
+ function registerModel(entry) {
25
+ if (store.models.has(entry.name)) {
26
+ throw new Error(`Model "${entry.name}" já registrada no Core.`);
27
+ }
28
+ store.models.set(entry.name, entry);
29
+ return entry;
30
+ }
31
+
32
+ function getModel(name) {
33
+ return store.models.get(name);
34
+ }
35
+
36
+ function listModels() {
37
+ return [...store.models.values()];
38
+ }
39
+
40
+ function registerValidation(modelName, fn) {
41
+ store.validations.set(modelName, fn);
42
+ }
43
+
44
+ function getValidation(modelName) {
45
+ return store.validations.get(modelName);
46
+ }
47
+
48
+ function registerTrigger(modelName, hooks) {
49
+ const current = store.triggers.get(modelName) || { before: [], after: [] };
50
+ if (hooks.before) current.before.push(hooks.before);
51
+ if (hooks.after) current.after.push(hooks.after);
52
+ store.triggers.set(modelName, current);
53
+ }
54
+
55
+ function getTriggers(modelName) {
56
+ return store.triggers.get(modelName) || { before: [], after: [] };
57
+ }
58
+
59
+ function registerRoutes(entry) {
60
+ store.routes.push(entry);
61
+ }
62
+
63
+ function listRoutes() {
64
+ return store.routes;
65
+ }
66
+
67
+ const simpleRegister = (mapName) => (key, value) => {
68
+ store[mapName].set(key, value);
69
+ return value;
70
+ };
71
+ const simpleList = (mapName) => () => [...store[mapName].values()];
72
+
73
+ function registerMenu(item) {
74
+ store.menus.push(item);
75
+ }
76
+
77
+ function reset() {
78
+ store.models.clear();
79
+ store.validations.clear();
80
+ store.triggers.clear();
81
+ store.routes.length = 0;
82
+ store.collections.clear();
83
+ store.documents.clear();
84
+ store.pipelines.clear();
85
+ store.omieMappings.clear();
86
+ store.integrations.clear();
87
+ store.menus.length = 0;
88
+ store.features.clear();
89
+ }
90
+
91
+ module.exports = {
92
+ store,
93
+ registerModel,
94
+ getModel,
95
+ listModels,
96
+ registerValidation,
97
+ getValidation,
98
+ registerTrigger,
99
+ getTriggers,
100
+ registerRoutes,
101
+ listRoutes,
102
+ registerCollection: simpleRegister("collections"),
103
+ listCollections: simpleList("collections"),
104
+ registerDocument: simpleRegister("documents"),
105
+ listDocuments: simpleList("documents"),
106
+ registerPipeline: simpleRegister("pipelines"),
107
+ listPipelines: simpleList("pipelines"),
108
+ registerOmieMapping: simpleRegister("omieMappings"),
109
+ listOmieMappings: simpleList("omieMappings"),
110
+ registerIntegration: simpleRegister("integrations"),
111
+ listIntegrations: simpleList("integrations"),
112
+ registerFeature: simpleRegister("features"),
113
+ listFeatures: simpleList("features"),
114
+ registerMenu,
115
+ listMenus: () => store.menus,
116
+ reset,
117
+ };
@@ -0,0 +1,44 @@
1
+ exports.sendResponse = ({ res, statusCode = 200, message = "OK", ...rest }) => {
2
+ res.status(statusCode).send({ message, ...rest });
3
+ };
4
+
5
+ exports.sendErrorResponse = ({ res, statusCode = 500, message, error }) => {
6
+ res.status(statusCode).json({
7
+ message,
8
+ ...(error ? { error } : {}),
9
+ });
10
+ };
11
+
12
+ exports.sendPaginatedResponse = ({
13
+ res,
14
+ statusCode = 200,
15
+ message = "OK",
16
+ results,
17
+ pagination: { currentPage, totalPages, totalItems, itemsPerPage },
18
+ }) => {
19
+ res.status(statusCode).json({
20
+ message,
21
+ results,
22
+ pagination: { currentPage, totalPages, totalItems, itemsPerPage },
23
+ });
24
+ };
25
+
26
+ exports.asyncHandler = (callback) => {
27
+ return (req, res, next) => {
28
+ Promise.resolve(callback(req, res, next)).catch((error) => {
29
+ // Erros de domínio (4xx) são esperados; só logamos os inesperados.
30
+ const status = error?.statusCode || 500;
31
+ if (status >= 500) console.log("🆘 [ERROR]:", error?.message || error);
32
+ next(error);
33
+ });
34
+ };
35
+ };
36
+
37
+ exports.attempt = async (callback) => {
38
+ try {
39
+ const result = await callback();
40
+ return [result, null];
41
+ } catch (err) {
42
+ return [null, err];
43
+ }
44
+ };
@@ -0,0 +1,88 @@
1
+ const fs = require("node:fs");
2
+ const path = require("node:path");
3
+
4
+ const TEMPLATES_DIR = path.join(__dirname, "templates");
5
+
6
+ /**
7
+ * Defaults dos manifests. São sobrescritos pelo `central.manifest.json`
8
+ * do consumidor. O dev não escreve YAML — só ajusta esses valores.
9
+ */
10
+ const DEFAULTS = {
11
+ SERVICE_NAME: "central-oon",
12
+ NAMESPACE: "default",
13
+ IMAGE: "ghcr.io/oondemand/central-oon",
14
+ IMAGE_TAG: "latest",
15
+ PORT: "4000",
16
+ REPLICAS: "1",
17
+ NODE_ENV: "production",
18
+ MEM_REQUEST: "128Mi",
19
+ MEM_LIMIT: "256Mi",
20
+ CPU_REQUEST: "0.1",
21
+ CPU_LIMIT: "0.2",
22
+ HPA_MIN: "1",
23
+ HPA_MAX: "3",
24
+ HPA_CPU_TARGET: "70",
25
+ HOST: "central.example.com",
26
+ DB_SERVER: "",
27
+ DB_NAME: "",
28
+ DB_AUTH_SOURCE: "",
29
+ DB_REPLICA_SET: "",
30
+ DB_TSL: "",
31
+ DB_USER: "",
32
+ DB_PASSWORD: "",
33
+ JWT_SECRET: "",
34
+ OMIE_APP_KEY: "",
35
+ OMIE_APP_SECRET: "",
36
+ MEUS_APPS_BACKEND_URL: "",
37
+ };
38
+
39
+ const K8S_TEMPLATES = [
40
+ "configmap.yaml",
41
+ "secret.yaml",
42
+ "deployment.yaml",
43
+ "service.yaml",
44
+ "ingress.yaml",
45
+ "hpa.yaml",
46
+ "certificate.yaml",
47
+ ];
48
+
49
+ function interpolate(content, vars) {
50
+ return content.replace(/\$\{(\w+)\}/g, (_, key) =>
51
+ vars[key] !== undefined ? String(vars[key]) : ""
52
+ );
53
+ }
54
+
55
+ function loadManifest(cwd = process.cwd()) {
56
+ const file = path.join(cwd, "central.manifest.json");
57
+ const user = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, "utf8")) : {};
58
+ return { ...DEFAULTS, ...user };
59
+ }
60
+
61
+ /**
62
+ * Renderiza os manifests k8s para `outDir` (default: ./k8s) a partir do
63
+ * central.manifest.json. Retorna a lista de arquivos gerados.
64
+ */
65
+ function renderK8s({ cwd = process.cwd(), outDir } = {}) {
66
+ const vars = loadManifest(cwd);
67
+ const target = outDir || path.join(cwd, "k8s");
68
+ fs.mkdirSync(target, { recursive: true });
69
+
70
+ const written = [];
71
+ for (const tpl of K8S_TEMPLATES) {
72
+ const raw = fs.readFileSync(path.join(TEMPLATES_DIR, tpl), "utf8");
73
+ const out = interpolate(raw, vars);
74
+ const dest = path.join(target, tpl);
75
+ fs.writeFileSync(dest, out);
76
+ written.push(dest);
77
+ }
78
+ return written;
79
+ }
80
+
81
+ function renderDockerfile({ cwd = process.cwd() } = {}) {
82
+ const raw = fs.readFileSync(path.join(TEMPLATES_DIR, "Dockerfile"), "utf8");
83
+ const dest = path.join(cwd, "Dockerfile");
84
+ fs.writeFileSync(dest, raw);
85
+ return dest;
86
+ }
87
+
88
+ module.exports = { renderK8s, renderDockerfile, loadManifest, DEFAULTS, TEMPLATES_DIR };
@@ -0,0 +1,19 @@
1
+ # Dockerfile padrão de uma Central Oon (gerado pelo oonCore-back).
2
+ # Estágio 1: dependências de produção
3
+ FROM node:18 AS builder
4
+ WORKDIR /app
5
+ COPY package*.json ./
6
+ RUN npm install --production
7
+ COPY src ./src
8
+ COPY central.config.js ./central.config.js
9
+
10
+ # Estágio 2: runtime
11
+ FROM node:18-alpine
12
+ ARG SERVICE_VERSION
13
+ ENV SERVICE_VERSION=${SERVICE_VERSION}
14
+ WORKDIR /app
15
+ COPY --from=builder /app .
16
+ RUN mkdir -p uploads
17
+ EXPOSE 4000
18
+ # Sobe a Central via CLI do Core.
19
+ CMD ["npx", "oonCore-back", "start"]
@@ -0,0 +1,12 @@
1
+ apiVersion: cert-manager.io/v1
2
+ kind: Certificate
3
+ metadata:
4
+ name: ${SERVICE_NAME}-cert
5
+ namespace: ${NAMESPACE}
6
+ spec:
7
+ secretName: ${SERVICE_NAME}-tls
8
+ issuerRef:
9
+ name: letsencrypt-prod
10
+ kind: ClusterIssuer
11
+ dnsNames:
12
+ - ${HOST}
@@ -0,0 +1,16 @@
1
+ apiVersion: v1
2
+ kind: ConfigMap
3
+ metadata:
4
+ name: ${SERVICE_NAME}-config
5
+ namespace: ${NAMESPACE}
6
+ data:
7
+ NODE_ENV: "${NODE_ENV}"
8
+ SERVICE_NAME: "${SERVICE_NAME}"
9
+ SERVICE_VERSION: "${IMAGE_TAG}"
10
+ PORT: "${PORT}"
11
+ DB_SERVER: "${DB_SERVER}"
12
+ DB_NAME: "${DB_NAME}"
13
+ DB_AUTH_SOURCE: "${DB_AUTH_SOURCE}"
14
+ DB_REPLICA_SET: "${DB_REPLICA_SET}"
15
+ DB_TSL: "${DB_TSL}"
16
+ MEUS_APPS_BACKEND_URL: "${MEUS_APPS_BACKEND_URL}"
@@ -0,0 +1,32 @@
1
+ apiVersion: apps/v1
2
+ kind: Deployment
3
+ metadata:
4
+ name: ${SERVICE_NAME}
5
+ namespace: ${NAMESPACE}
6
+ spec:
7
+ replicas: ${REPLICAS}
8
+ selector:
9
+ matchLabels:
10
+ app: ${SERVICE_NAME}
11
+ template:
12
+ metadata:
13
+ labels:
14
+ app: ${SERVICE_NAME}
15
+ spec:
16
+ containers:
17
+ - name: ${SERVICE_NAME}-container
18
+ image: ${IMAGE}:${IMAGE_TAG}
19
+ ports:
20
+ - containerPort: ${PORT}
21
+ resources:
22
+ requests:
23
+ memory: "${MEM_REQUEST}"
24
+ cpu: "${CPU_REQUEST}"
25
+ limits:
26
+ memory: "${MEM_LIMIT}"
27
+ cpu: "${CPU_LIMIT}"
28
+ envFrom:
29
+ - configMapRef:
30
+ name: ${SERVICE_NAME}-config
31
+ - secretRef:
32
+ name: ${SERVICE_NAME}-secret
@@ -0,0 +1,19 @@
1
+ apiVersion: autoscaling/v2
2
+ kind: HorizontalPodAutoscaler
3
+ metadata:
4
+ name: ${SERVICE_NAME}-hpa
5
+ namespace: ${NAMESPACE}
6
+ spec:
7
+ scaleTargetRef:
8
+ apiVersion: apps/v1
9
+ kind: Deployment
10
+ name: ${SERVICE_NAME}
11
+ minReplicas: ${HPA_MIN}
12
+ maxReplicas: ${HPA_MAX}
13
+ metrics:
14
+ - type: Resource
15
+ resource:
16
+ name: cpu
17
+ target:
18
+ type: Utilization
19
+ averageUtilization: ${HPA_CPU_TARGET}
@@ -0,0 +1,24 @@
1
+ apiVersion: networking.k8s.io/v1
2
+ kind: Ingress
3
+ metadata:
4
+ name: ${SERVICE_NAME}-ingress
5
+ namespace: ${NAMESPACE}
6
+ annotations:
7
+ kubernetes.io/ingress.class: "nginx"
8
+ cert-manager.io/cluster-issuer: "letsencrypt-prod"
9
+ spec:
10
+ tls:
11
+ - hosts:
12
+ - ${HOST}
13
+ secretName: ${SERVICE_NAME}-tls
14
+ rules:
15
+ - host: ${HOST}
16
+ http:
17
+ paths:
18
+ - path: /
19
+ pathType: Prefix
20
+ backend:
21
+ service:
22
+ name: ${SERVICE_NAME}-service
23
+ port:
24
+ number: 80
@@ -0,0 +1,12 @@
1
+ apiVersion: v1
2
+ kind: Secret
3
+ metadata:
4
+ name: ${SERVICE_NAME}-secret
5
+ namespace: ${NAMESPACE}
6
+ type: Opaque
7
+ stringData:
8
+ DB_USER: "${DB_USER}"
9
+ DB_PASSWORD: "${DB_PASSWORD}"
10
+ JWT_SECRET: "${JWT_SECRET}"
11
+ OMIE_APP_KEY: "${OMIE_APP_KEY}"
12
+ OMIE_APP_SECRET: "${OMIE_APP_SECRET}"
@@ -0,0 +1,14 @@
1
+ apiVersion: v1
2
+ kind: Service
3
+ metadata:
4
+ name: ${SERVICE_NAME}-service
5
+ namespace: ${NAMESPACE}
6
+ spec:
7
+ type: ClusterIP
8
+ selector:
9
+ app: ${SERVICE_NAME}
10
+ ports:
11
+ - name: http
12
+ protocol: TCP
13
+ port: 80
14
+ targetPort: ${PORT}
package/src/index.js ADDED
@@ -0,0 +1,43 @@
1
+ require("dotenv").config();
2
+
3
+ /**
4
+ * API pública do @oondemand/oon-core-back (Seção 2.4).
5
+ *
6
+ * O consumidor escreve apenas models/validations/triggers/hooks/mappings/
7
+ * pipelines/documents/integrations/routes e sobe com `start()` ou
8
+ * `oonCore-back start`.
9
+ */
10
+ const { start } = require("./boot/startServer");
11
+ const { createApp } = require("./boot/createApp");
12
+ const { activate } = require("./boot/activation");
13
+ const define = require("./core/define");
14
+ const fields = require("./core/fields");
15
+ const registry = require("./core/registry");
16
+ const GenericError = require("./core/errors/GenericError");
17
+ const { omie } = require("./modules/omie");
18
+
19
+ module.exports = {
20
+ // boot
21
+ start,
22
+ createApp,
23
+ activate,
24
+
25
+ // declaração de domínio
26
+ defineCentral: define.defineCentral,
27
+ defineModel: define.defineModel,
28
+ defineCollection: define.defineCollection,
29
+ defineDocument: define.defineDocument,
30
+ definePipeline: define.definePipeline,
31
+ defineOmieMapping: define.defineOmieMapping,
32
+ defineRoutes: define.defineRoutes,
33
+ defineValidation: define.defineValidation,
34
+ defineTrigger: define.defineTrigger,
35
+
36
+ // helpers
37
+ fields,
38
+ omie,
39
+ GenericError,
40
+
41
+ // avançado / introspecção
42
+ registry,
43
+ };
@@ -0,0 +1,33 @@
1
+ const mongoose = require("mongoose");
2
+
3
+ /**
4
+ * Módulo Assistentes IA (Seção 13) — catálogo de assistentes, prompts e
5
+ * provedores. O dev só declara `ai: { ... }` na model/pipeline; o
6
+ * mascaramento e os guardrails ficam no Core.
7
+ */
8
+ const assistenteSchema = new mongoose.Schema(
9
+ {
10
+ nome: { type: String, required: true },
11
+ provedor: { type: String, default: "anthropic" },
12
+ modelo: { type: String, default: "claude-haiku-4-5-20251001" },
13
+ prompt: String,
14
+ ativo: { type: Boolean, default: true },
15
+ },
16
+ { timestamps: true }
17
+ );
18
+
19
+ const Assistente =
20
+ mongoose.models.Assistente || mongoose.model("Assistente", assistenteSchema);
21
+
22
+ /**
23
+ * Mascara campos sensíveis antes de enviar a um provedor de IA.
24
+ */
25
+ function mascarar(texto, segredos = []) {
26
+ let out = String(texto || "");
27
+ for (const s of segredos) {
28
+ if (s) out = out.split(s).join("«redacted»");
29
+ }
30
+ return out;
31
+ }
32
+
33
+ module.exports = { Assistente, mascarar };
@@ -0,0 +1,31 @@
1
+ const mongoose = require("mongoose");
2
+
3
+ /**
4
+ * Módulo Multi-moedas (Seção 14) — cadastro/cotação/conversão. As models
5
+ * usam os helpers `fields.currency*`. Aqui ficam a model interna de Moeda
6
+ * e o serviço de conversão padrão.
7
+ */
8
+ const moedaSchema = new mongoose.Schema(
9
+ {
10
+ codigo: { type: String, uppercase: true, minlength: 3, maxlength: 3, unique: true },
11
+ nome: String,
12
+ cotacao: { type: Number, default: 1 },
13
+ ativa: { type: Boolean, default: true },
14
+ },
15
+ { timestamps: true }
16
+ );
17
+
18
+ const Moeda = mongoose.models.Moeda || mongoose.model("Moeda", moedaSchema);
19
+
20
+ async function converter({ valor, de = "BRL", para = "BRL" }) {
21
+ if (de === para) return valor;
22
+ const [origem, destino] = await Promise.all([
23
+ Moeda.findOne({ codigo: de }),
24
+ Moeda.findOne({ codigo: para }),
25
+ ]);
26
+ const cotacaoDe = origem?.cotacao || 1;
27
+ const cotacaoPara = destino?.cotacao || 1;
28
+ return (valor * cotacaoDe) / cotacaoPara;
29
+ }
30
+
31
+ module.exports = { Moeda, converter };