@oondemand/oon-core-back 0.4.22 → 0.4.24
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.
- package/README.md +7 -0
- package/package.json +1 -1
- package/src/api/status.routes.js +52 -8
- package/src/core/utils/instanceActivation.js +54 -0
package/README.md
CHANGED
|
@@ -15,6 +15,13 @@ O Core suporta `ecosystem.role` em `central.config.js`: `root` para a Central de
|
|
|
15
15
|
|
|
16
16
|
Compatibilidade temporária: `CENTRAL_ATIVACAO_BACKEND_URL` e `MEUS_APPS_BACKEND_URL` continuam aceitas como aliases da URL da API. Nunca aponte a URL da API para o próprio Core.
|
|
17
17
|
|
|
18
|
+
## Saúde operacional
|
|
19
|
+
|
|
20
|
+
- `GET /health/ready`: retorna HTTP 200 somente quando o MongoDB está conectado; retorna 503 enquanto o runtime não estiver pronto.
|
|
21
|
+
- `GET /health/version`: expõe as versões do OonCore e da Central, commit, release, build e ambiente da publicação.
|
|
22
|
+
|
|
23
|
+
Na imagem de entrega, essas rotas são publicadas pelo Nginx sob `/api/health/ready` e `/api/health/version`.
|
|
24
|
+
|
|
18
25
|
## Fluxo
|
|
19
26
|
|
|
20
27
|
`GET /ativacao/status` informa estado sem segredos. `POST /ativacao/validar-codigo` valida sem consumir. `POST /ativacao/concluir` revalida, chama `/ativar`, criptografa imediatamente o token da instância, executa hooks declarativos, chama `/concluir` e marca a instância como `ativa`. `POST /ativacao/tentar-novamente` retoma uma ativação já registrada sem exigir novo código.
|
package/package.json
CHANGED
package/src/api/status.routes.js
CHANGED
|
@@ -1,21 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
1
3
|
const express = require("express");
|
|
2
4
|
const mongoose = require("mongoose");
|
|
3
5
|
const ctx = require("../core/context");
|
|
6
|
+
const { runtimeSummary } = require("./core.routes");
|
|
4
7
|
|
|
5
8
|
const router = express.Router();
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
const DATABASE_STATES = Object.freeze({
|
|
11
|
+
0: { code: "disconnected", label: "Desconectado" },
|
|
12
|
+
1: { code: "connected", label: "Conectado" },
|
|
13
|
+
2: { code: "connecting", label: "Conectando" },
|
|
14
|
+
3: { code: "disconnecting", label: "Desconectando" },
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function databaseState(readyState = mongoose.connection.readyState) {
|
|
18
|
+
return DATABASE_STATES[readyState] || {
|
|
19
|
+
code: "unknown",
|
|
20
|
+
label: "Status desconhecido",
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function readinessSummary(readyState = mongoose.connection.readyState) {
|
|
25
|
+
const database = databaseState(readyState);
|
|
26
|
+
const ready = readyState === 1;
|
|
27
|
+
return {
|
|
28
|
+
status: ready ? "ready" : "not_ready",
|
|
29
|
+
service: ctx.config.serviceName,
|
|
30
|
+
database: {
|
|
31
|
+
ready,
|
|
32
|
+
state: database.code,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function versionSummary() {
|
|
38
|
+
return {
|
|
39
|
+
status: "ok",
|
|
40
|
+
service: ctx.config.serviceName,
|
|
41
|
+
...runtimeSummary(),
|
|
13
42
|
};
|
|
14
|
-
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
router.get("/", (_req, res) => {
|
|
46
|
+
const database = databaseState();
|
|
15
47
|
res.status(200).json({
|
|
16
48
|
message: `${ctx.config.serviceName} rodando - vs ${ctx.config.serviceVersion}`,
|
|
17
|
-
database:
|
|
49
|
+
database: database.label,
|
|
18
50
|
});
|
|
19
51
|
});
|
|
20
52
|
|
|
53
|
+
router.get("/health/ready", (_req, res) => {
|
|
54
|
+
const summary = readinessSummary();
|
|
55
|
+
res.status(summary.database.ready ? 200 : 503).json(summary);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
router.get("/health/version", (_req, res) => {
|
|
59
|
+
res.status(200).json(versionSummary());
|
|
60
|
+
});
|
|
61
|
+
|
|
21
62
|
module.exports = router;
|
|
63
|
+
module.exports.databaseState = databaseState;
|
|
64
|
+
module.exports.readinessSummary = readinessSummary;
|
|
65
|
+
module.exports.versionSummary = versionSummary;
|
|
@@ -114,6 +114,27 @@ function role() {
|
|
|
114
114
|
return ctx.config.ecosystem?.role === "root" ? "root" : "member";
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
function deploymentBootstrap() {
|
|
118
|
+
const deploymentId = String(process.env.OON_DEPLOYMENT_ID || "").trim();
|
|
119
|
+
const token = String(process.env.OON_DEPLOYMENT_TOKEN || "").trim();
|
|
120
|
+
if (!deploymentId && !token) return null;
|
|
121
|
+
if (!deploymentId || !token) {
|
|
122
|
+
throw new GenericError(
|
|
123
|
+
"OON_DEPLOYMENT_ID e OON_DEPLOYMENT_TOKEN devem ser configurados juntos.",
|
|
124
|
+
{ statusCode: 500, code: "DEPLOYMENT_BOOTSTRAP_INCOMPLETE" },
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const tenancyModel =
|
|
128
|
+
ctx.config.tenancyModel || ctx.config.ecosystem?.tenancyModel;
|
|
129
|
+
if (tenancyModel !== "none") {
|
|
130
|
+
throw new GenericError(
|
|
131
|
+
"Bootstrap direto de deployment é permitido somente para Apps globais.",
|
|
132
|
+
{ statusCode: 500, code: "DEPLOYMENT_BOOTSTRAP_TENANCY_INVALID" },
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
return { deploymentId, token };
|
|
136
|
+
}
|
|
137
|
+
|
|
117
138
|
function ambiente() {
|
|
118
139
|
const value =
|
|
119
140
|
process.env.APP_ENVIRONMENT || process.env.NODE_ENV || "desenvolvimento";
|
|
@@ -264,6 +285,38 @@ async function ensureInstance() {
|
|
|
264
285
|
await instance.save();
|
|
265
286
|
}
|
|
266
287
|
|
|
288
|
+
const bootstrap = deploymentBootstrap();
|
|
289
|
+
if (bootstrap) {
|
|
290
|
+
let currentToken = null;
|
|
291
|
+
try {
|
|
292
|
+
currentToken = decrypt(instance);
|
|
293
|
+
} catch {
|
|
294
|
+
currentToken = null;
|
|
295
|
+
}
|
|
296
|
+
if (
|
|
297
|
+
instance.deploymentId !== bootstrap.deploymentId ||
|
|
298
|
+
instance.credentialType !== "deployment" ||
|
|
299
|
+
currentToken !== bootstrap.token ||
|
|
300
|
+
instance.status !== "ativa"
|
|
301
|
+
) {
|
|
302
|
+
clearInstanceIdentity(instance);
|
|
303
|
+
instance.deploymentId = bootstrap.deploymentId;
|
|
304
|
+
instance.credentialType = "deployment";
|
|
305
|
+
instance.status = "ativa";
|
|
306
|
+
instance.appCode = getAppCode();
|
|
307
|
+
instance.ambiente = ambiente();
|
|
308
|
+
instance.ecosystemUrl = process.env.CENTRAL_ATIVACAO_URL;
|
|
309
|
+
instance.publicUrl = process.env.PUBLIC_APP_URL;
|
|
310
|
+
instance.coreVersion = version();
|
|
311
|
+
instance.appVersion = version();
|
|
312
|
+
instance.ativadaEm = new Date();
|
|
313
|
+
instance.concluidaEm = new Date();
|
|
314
|
+
instance.credentialVersion = Number(instance.credentialVersion || 0) + 1;
|
|
315
|
+
Object.assign(instance, encrypt(bootstrap.token));
|
|
316
|
+
await instance.save();
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
267
320
|
// Versões anteriores do Core marcavam a aplicação root como ativa sem criar
|
|
268
321
|
// o App root e o grafo mínimo do ecossistema. Quando o domínio disponibiliza
|
|
269
322
|
// o verificador, o App cadastrado passa a ser a fonte de verdade.
|
|
@@ -530,6 +583,7 @@ module.exports = {
|
|
|
530
583
|
globalFields,
|
|
531
584
|
rootFields,
|
|
532
585
|
role,
|
|
586
|
+
deploymentBootstrap,
|
|
533
587
|
ambiente,
|
|
534
588
|
publicUrl,
|
|
535
589
|
version,
|