miolo 3.0.0-beta.21 → 3.0.0-beta.210
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/bin/build/build.mjs +53 -0
- package/bin/build/build_bin.mjs +17 -0
- package/bin/build/cli/client.mjs +52 -0
- package/bin/build/cli/css.mjs +22 -0
- package/bin/build/cli/index.mjs +40 -0
- package/bin/build/cli/ssr.mjs +21 -0
- package/bin/build/server/aliases.mjs +112 -0
- package/bin/build/server/babel.config.js +24 -0
- package/bin/build/server/banner.mjs +20 -0
- package/bin/build/server/bundle.mjs +111 -0
- package/bin/build/server/fix.mjs +15 -0
- package/bin/build/server/index.mjs +69 -0
- package/bin/build/server/options.mjs +83 -0
- package/bin/create/auth.mjs +23 -0
- package/bin/create/copy.mjs +175 -0
- package/bin/create/docker.mjs +25 -0
- package/bin/create/index.mjs +137 -0
- package/bin/create/pkgjson.mjs +72 -0
- package/bin/create/prepare-template.mjs +158 -0
- package/bin/create/validation.mjs +27 -0
- package/bin/dev/dev.mjs +32 -23
- package/bin/dev/dev_start.mjs +6 -5
- package/bin/index.mjs +94 -52
- package/bin/prod-bin/create-bin.mjs +42 -27
- package/bin/prod-bin/run.mjs +13 -9
- package/bin/{prod-run → run}/pid.mjs +4 -4
- package/bin/run/restart.mjs +13 -0
- package/bin/run/start.mjs +18 -0
- package/bin/run/stop.mjs +20 -0
- package/bin/util.mjs +35 -11
- package/package.json +59 -39
- package/src/config/.env +34 -12
- package/src/config/defaults.mjs +253 -185
- package/src/config/env.mjs +40 -22
- package/src/config/index.mjs +19 -24
- package/src/config/util.mjs +25 -10
- package/src/db-conn.mjs +34 -0
- package/src/engines/cron/emails.mjs +10 -5
- package/src/engines/cron/index.mjs +45 -51
- package/src/engines/cron/init.mjs +16 -17
- package/src/engines/cron/ipsum.mjs +65 -60
- package/src/engines/cron/syscheck.mjs +30 -30
- package/src/engines/emailer/index.mjs +1 -2
- package/src/engines/emailer/queue.mjs +14 -20
- package/src/engines/emailer/transporter.mjs +86 -74
- package/src/engines/geoip/index.mjs +23 -28
- package/src/engines/http/index.mjs +26 -15
- package/src/engines/logger/buildErrorEmailBody.mjs +72 -0
- package/src/engines/logger/index.mjs +114 -122
- package/src/engines/logger/injectStackTrace.mjs +59 -0
- package/src/engines/logger/logger_mail.mjs +47 -61
- package/src/engines/logger/reopenTransportOnHupSignal.mjs +12 -13
- package/src/engines/parser/Parser.mjs +77 -60
- package/src/engines/parser/index.mjs +1 -1
- package/src/engines/schema/diffObjs.mjs +41 -0
- package/src/engines/schema/index.mjs +4 -0
- package/src/engines/schema/input.mjs +54 -0
- package/src/engines/schema/output.mjs +66 -0
- package/src/engines/socket/index.mjs +44 -46
- package/src/index.mjs +15 -10
- package/src/middleware/auth/basic.mjs +41 -40
- package/src/middleware/auth/custom.mjs +10 -13
- package/src/middleware/auth/guest.mjs +27 -27
- package/src/middleware/auth/passport/index.mjs +374 -0
- package/src/middleware/auth/passport/session/index.mjs +43 -0
- package/src/middleware/auth/{credentials → passport}/session/store.mjs +35 -15
- package/src/middleware/auth/passport/session/store_koa_redis.mjs +3 -0
- package/src/middleware/context/cache/index.mjs +78 -33
- package/src/middleware/context/cache/options.mjs +19 -21
- package/src/middleware/context/db.mjs +45 -20
- package/src/middleware/context/index.mjs +12 -12
- package/src/middleware/extra.mjs +4 -5
- package/src/middleware/http/body.mjs +25 -25
- package/src/middleware/http/catcher.mjs +81 -8
- package/src/middleware/http/custom_blacklist.mjs +19 -16
- package/src/middleware/http/headers.mjs +37 -34
- package/src/middleware/http/ratelimit.mjs +16 -23
- package/src/middleware/http/request.mjs +60 -65
- package/src/middleware/routes/catch_js_error.mjs +30 -23
- package/src/middleware/routes/robots.mjs +4 -7
- package/src/middleware/routes/router/crud/attachCrudRoutes.mjs +108 -90
- package/src/middleware/routes/router/crud/getCrudConfig.mjs +31 -55
- package/src/middleware/routes/router/defaults.mjs +6 -19
- package/src/middleware/routes/router/index.mjs +17 -21
- package/src/middleware/routes/router/queries/attachQueriesRoutes.mjs +227 -50
- package/src/middleware/routes/router/queries/getQueriesConfig.mjs +45 -55
- package/src/middleware/routes/router/utils.mjs +41 -26
- package/src/middleware/ssr/context.mjs +5 -7
- package/src/middleware/ssr/html.mjs +66 -43
- package/src/middleware/ssr/loader.mjs +11 -14
- package/src/middleware/ssr/ssr_render.mjs +39 -22
- package/src/middleware/static/index.mjs +33 -14
- package/src/middleware/vite/devserver.mjs +38 -22
- package/src/middleware/vite/watcher.mjs +12 -14
- package/src/server-cron.mjs +13 -8
- package/src/server-dev.mjs +13 -16
- package/src/server.mjs +49 -51
- package/template/.agent/skills/miolo-app-arch/SKILL.md +218 -0
- package/template/.agent/skills/miolo-auth/SKILL.md +450 -0
- package/template/.agent/skills/miolo-cli-router/SKILL.md +394 -0
- package/template/.agent/skills/miolo-database/SKILL.md +358 -0
- package/template/.agent/skills/miolo-react-patterns/SKILL.md +426 -0
- package/template/.agent/skills/miolo-routing/SKILL.md +326 -0
- package/template/.agent/skills/miolo-schemas/SKILL.md +329 -0
- package/template/.agent/skills/miolo-session-context/SKILL.md +397 -0
- package/template/.agent/skills/miolo-ssr/SKILL.md +433 -0
- package/template/.editorconfig +18 -0
- package/template/.env +120 -0
- package/template/biome.json +63 -0
- package/template/components.json +21 -0
- package/template/db/init.sh +89 -0
- package/template/db/sql/00_drop.sql +2 -0
- package/template/db/sql/01_users.sql +31 -0
- package/template/db/sql/02_todos.sql +20 -0
- package/template/docker/Dockerfile +13 -0
- package/template/docker/docker-compose.yaml +79 -0
- package/template/gitignore +42 -0
- package/template/jsconfig.json +18 -0
- package/template/package.json +88 -0
- package/template/postcss.config.js +9 -0
- package/template/src/cli/App.jsx +25 -0
- package/template/src/cli/components/JsonTreeViewer.jsx +128 -0
- package/template/src/cli/components/shadcn-io/spinner/index.jsx +232 -0
- package/template/src/cli/components/stepper.jsx +408 -0
- package/template/src/cli/components/ui/avatar.jsx +36 -0
- package/template/src/cli/components/ui/badge.jsx +31 -0
- package/template/src/cli/components/ui/breadcrumb.jsx +97 -0
- package/template/src/cli/components/ui/card.jsx +73 -0
- package/template/src/cli/components/ui/collapsible.jsx +16 -0
- package/template/src/cli/components/ui/dropdown-menu.jsx +179 -0
- package/template/src/cli/components/ui/field.jsx +217 -0
- package/template/src/cli/components/ui/input.jsx +19 -0
- package/template/src/cli/components/ui/label.jsx +17 -0
- package/template/src/cli/components/ui/pagination.jsx +99 -0
- package/template/src/cli/components/ui/patched/alert.jsx +56 -0
- package/template/src/cli/components/ui/patched/button.jsx +45 -0
- package/template/src/cli/components/ui/patched/dialog.jsx +114 -0
- package/template/src/cli/components/ui/patched/sidebar.jsx +660 -0
- package/template/src/cli/components/ui/select.jsx +141 -0
- package/template/src/cli/components/ui/separator.jsx +21 -0
- package/template/src/cli/components/ui/sheet.jsx +115 -0
- package/template/src/cli/components/ui/skeleton.jsx +13 -0
- package/template/src/cli/components/ui/sonner.jsx +22 -0
- package/template/src/cli/components/ui/switch.jsx +25 -0
- package/template/src/cli/components/ui/table.jsx +88 -0
- package/template/src/cli/components/ui/textarea.jsx +16 -0
- package/template/src/cli/components/ui/tooltip.jsx +45 -0
- package/template/src/cli/config/store_keys.mjs +2 -0
- package/template/src/cli/context/data/DataContext.jsx +5 -0
- package/template/src/cli/context/data/DataProvider.jsx +44 -0
- package/template/src/cli/context/data/useBreads.mjs +15 -0
- package/template/src/cli/context/data/useDataContext.mjs +4 -0
- package/template/src/cli/context/session/SessionContext.mjs +4 -0
- package/template/src/cli/context/session/SessionProvider.jsx +31 -0
- package/template/src/cli/context/session/makePermissioner.mjs +34 -0
- package/template/src/cli/context/session/useSessionContext.mjs +6 -0
- package/template/src/cli/context/theme/ThemeContext.mjs +4 -0
- package/template/src/cli/context/theme/ThemeProvider.jsx +49 -0
- package/template/src/cli/context/theme/useThemeContext.mjs +6 -0
- package/template/src/cli/context/ui/UIContext.jsx +5 -0
- package/template/src/cli/context/ui/UIProvider.jsx +16 -0
- package/template/src/cli/context/ui/useUIContext.mjs +4 -0
- package/template/src/cli/context/util.mjs +17 -0
- package/template/src/cli/entry-cli.jsx +33 -0
- package/template/src/cli/hooks/useIsMobile.mjs +19 -0
- package/template/src/cli/hooks/useStoragedState.mjs +63 -0
- package/template/src/cli/index.html +29 -0
- package/template/src/cli/layout/app-sidebar.jsx +25 -0
- package/template/src/cli/layout/main-layout.jsx +63 -0
- package/template/src/cli/layout/nav-last-todos.jsx +72 -0
- package/template/src/cli/layout/nav-main.jsx +39 -0
- package/template/src/cli/layout/nav-user.jsx +105 -0
- package/template/src/cli/layout/prop-switcher.jsx +93 -0
- package/template/src/cli/lib/utils.mjs +10 -0
- package/template/src/cli/pages/Index.jsx +13 -0
- package/template/src/cli/pages/IndexOffline.jsx +13 -0
- package/template/src/cli/pages/IndexOnline.jsx +18 -0
- package/template/src/cli/pages/dash/Dashboard.jsx +29 -0
- package/template/src/cli/pages/offline/Login.jsx +43 -0
- package/template/src/cli/pages/offline/LoginForm.jsx +115 -0
- package/template/src/cli/pages/security/Security.jsx +39 -0
- package/template/src/cli/pages/security/SecurityForm.jsx +106 -0
- package/template/src/cli/pages/todos/TodoActions.jsx +99 -0
- package/template/src/cli/pages/todos/TodoAdd.jsx +43 -0
- package/template/src/cli/pages/todos/TodoList.jsx +60 -0
- package/template/src/cli/pages/todos/Todos.jsx +23 -0
- package/template/src/cli/pages/todos/context/TodosContext.jsx +5 -0
- package/template/src/cli/pages/todos/context/TodosProvider.jsx +191 -0
- package/template/src/cli/pages/todos/context/useTodosContext.mjs +4 -0
- package/template/src/ns/models/Todo.mjs +29 -0
- package/template/src/ns/models/TodoList.mjs +8 -0
- package/template/src/ns/models/User.mjs +40 -0
- package/template/src/server/bot/check_today.mjs +10 -0
- package/template/src/server/io/cache/base.mjs +21 -0
- package/template/src/server/io/db/filter.mjs +92 -0
- package/template/src/server/io/db/todos/delete.mjs +29 -0
- package/template/src/server/io/db/todos/find.mjs +13 -0
- package/template/src/server/io/db/todos/read.mjs +83 -0
- package/template/src/server/io/db/todos/toggle.mjs +37 -0
- package/template/src/server/io/db/todos/upsave.mjs +32 -0
- package/template/src/server/io/db/triggers/user.mjs +13 -0
- package/template/src/server/io/db/users/auth.mjs +132 -0
- package/template/src/server/io/db/users/pwd.mjs +38 -0
- package/template/src/server/io/db/users/save.mjs +17 -0
- package/template/src/server/miolo/auth/basic.mjs +15 -0
- package/template/src/server/miolo/auth/guest.mjs +3 -0
- package/template/src/server/miolo/auth/passport.mjs +73 -0
- package/template/src/server/miolo/cache.mjs +11 -0
- package/template/src/server/miolo/cron/foo.mjs +7 -0
- package/template/src/server/miolo/cron/index.mjs +28 -0
- package/template/src/server/miolo/cron/invalidate.mjs +21 -0
- package/template/src/server/miolo/db.mjs +36 -0
- package/template/src/server/miolo/http.mjs +14 -0
- package/template/src/server/miolo/index.mjs +43 -0
- package/template/src/server/miolo/routes/crud.mjs +16 -0
- package/template/src/server/miolo/routes/index.mjs +8 -0
- package/template/src/server/miolo/ssr/entry-server.jsx +13 -0
- package/template/src/server/miolo/ssr/loader.mjs +18 -0
- package/template/src/server/routes/index.mjs +66 -0
- package/template/src/server/routes/todos/mod.mjs +52 -0
- package/template/src/server/routes/todos/read.mjs +45 -0
- package/template/src/server/routes/todos/special.mjs +47 -0
- package/template/src/server/routes/users/user.mjs +54 -0
- package/template/src/server/server.mjs +10 -0
- package/template/src/server/utils/crypt.mjs +38 -0
- package/template/src/server/utils/io.mjs +15 -0
- package/template/src/server/utils/pwdfor.mjs +25 -0
- package/template/src/server/utils/schema.mjs +22 -0
- package/template/src/static/img/default/profile.png +0 -0
- package/template/src/static/img/favicon.ico +0 -0
- package/template/src/static/img/miolo_logo.png +0 -0
- package/template/src/static/img/miolo_name.png +0 -0
- package/template/src/static/public/manifest.json +21 -0
- package/template/src/static/public/sw.js +79 -0
- package/template/src/static/style/globals.css +156 -0
- package/template/src/static/style/json-tree.css +54 -0
- package/template/src/static/style/skeleton.css +49 -0
- package/bin/prod-build/build-client.mjs +0 -67
- package/bin/prod-build/build-server.mjs +0 -58
- package/bin/prod-run/restart.mjs +0 -9
- package/bin/prod-run/start.mjs +0 -15
- package/bin/prod-run/stop.mjs +0 -20
- package/src/engines/logger/verify.mjs +0 -22
- package/src/middleware/auth/credentials/index.mjs +0 -151
- package/src/middleware/auth/credentials/session/index.mjs +0 -24
- package/src/middleware/auth/credentials/session/store_koa_redis.mjs +0 -3
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { format } from "winston"
|
|
2
|
+
|
|
3
|
+
const injectStackTrace = format((info) => {
|
|
4
|
+
// Solo nos interesa inyectar stack si es nivel error y NO tiene stack
|
|
5
|
+
if (info.level === "error" && !info.stack) {
|
|
6
|
+
const syntheticError = new Error(info.message)
|
|
7
|
+
const stackLines = syntheticError.stack.split("\n")
|
|
8
|
+
|
|
9
|
+
// 1. Definimos nuestro "Ancla".
|
|
10
|
+
// Es el archivo que actúa de frontera entre tu framework y el código de negocio.
|
|
11
|
+
// Usamos una cadena parcial única para identificarlo.
|
|
12
|
+
// DEV: 'src/engines/logger/index.mjs'
|
|
13
|
+
// PROD: ''
|
|
14
|
+
|
|
15
|
+
// 2. Buscamos en qué línea del stack aparece tu wrapper
|
|
16
|
+
let wrapperIndex = stackLines.findIndex((line) => line.includes("src/engines/logger/index.mjs"))
|
|
17
|
+
if (wrapperIndex === -1) {
|
|
18
|
+
for (const [lidx, l] of stackLines.entries()) {
|
|
19
|
+
if (
|
|
20
|
+
l.includes(
|
|
21
|
+
`${process.env.MIOLO_NAME || "miolo"}.${process.env.MIOLO_BUILD_SERVER_EXT || "node.bundle.mjs"}`
|
|
22
|
+
)
|
|
23
|
+
) {
|
|
24
|
+
const segs = ["Format.transform", "DerivedLogger"]
|
|
25
|
+
segs.forEach((seg) => {
|
|
26
|
+
if (l.includes(seg)) {
|
|
27
|
+
wrapperIndex = lidx
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let cleanStackLines = stackLines
|
|
35
|
+
|
|
36
|
+
if (wrapperIndex !== -1) {
|
|
37
|
+
// CASO ÉXITO: Encontramos tu wrapper.
|
|
38
|
+
// El stack útil empieza justo en la línea SIGUIENTE (+1).
|
|
39
|
+
// Mantenemos la cabecera (línea 0) y pegamos del wrapper hacia abajo.
|
|
40
|
+
cleanStackLines = [stackLines[0], ...stackLines.slice(wrapperIndex + 1)]
|
|
41
|
+
}
|
|
42
|
+
// FALLBACK: Si por alguna razón extraña no pasa por tu wrapper,
|
|
43
|
+
// hacemos una limpieza conservadora básica (winston y node internals).
|
|
44
|
+
cleanStackLines = cleanStackLines.filter(
|
|
45
|
+
(line) =>
|
|
46
|
+
!line.includes("node_modules/winston") &&
|
|
47
|
+
!line.includes("node_modules/logform") &&
|
|
48
|
+
!line.includes("node_modules/koa-compose") &&
|
|
49
|
+
!line.includes("node:internal") &&
|
|
50
|
+
!line.includes("node_modules/readable-stream") &&
|
|
51
|
+
!line.includes("injectStackTrace.mjs")
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
info.stack = cleanStackLines.join("\n")
|
|
55
|
+
}
|
|
56
|
+
return info
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
export default injectStackTrace
|
|
@@ -1,89 +1,75 @@
|
|
|
1
1
|
// "use strict";
|
|
2
|
-
import util from 'util'
|
|
3
|
-
import winston from 'winston'
|
|
4
|
-
import { uncolor } from 'tinguir'
|
|
5
|
-
|
|
6
|
-
function init_logger_to_mail(config, emailer) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
let MailerLogger = function (options) {
|
|
10
|
-
winston.Transport.call(this, options);
|
|
11
|
-
options = options || {};
|
|
12
|
-
|
|
13
|
-
this.level = config.level || 'info'
|
|
14
|
-
this.ename = config?.name || emailer.defaults.name
|
|
15
|
-
this.to = config.to || emailer.defaults.to
|
|
16
|
-
this.from = config.from || emailer.defaults.from
|
|
17
|
-
this.humanReadableUnhandledException = options.humanReadableUnhandledException || true;
|
|
18
|
-
this.handleExceptions = options.handleExceptions || true;
|
|
19
|
-
this.json = options.json || false;
|
|
20
|
-
this.colorize = options.colorize || false;
|
|
21
2
|
|
|
3
|
+
import util from "node:util"
|
|
4
|
+
import { uncolor } from "tinguir"
|
|
5
|
+
import winston from "winston"
|
|
6
|
+
import buildErrorEmailBody from "./buildErrorEmailBody.mjs"
|
|
22
7
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
8
|
+
function init_logger_to_mail(config, emailer) {
|
|
9
|
+
const MailerLogger = function (options) {
|
|
10
|
+
winston.Transport.call(this, options)
|
|
11
|
+
options = options || {}
|
|
12
|
+
|
|
13
|
+
this.level = config.level || "info"
|
|
14
|
+
this.ename = config?.name || emailer.defaults.name
|
|
15
|
+
this.to = config.to || emailer.defaults.to
|
|
16
|
+
this.from = config.from || emailer.defaults.from
|
|
17
|
+
this.humanReadableUnhandledException = options.humanReadableUnhandledException || true
|
|
18
|
+
this.handleExceptions = options.handleExceptions || true
|
|
19
|
+
this.json = options.json || false
|
|
20
|
+
this.colorize = options.colorize || false
|
|
21
|
+
}
|
|
26
22
|
|
|
27
23
|
/** @extends winston.Transport */
|
|
28
|
-
util.inherits(MailerLogger, winston.Transport)
|
|
24
|
+
util.inherits(MailerLogger, winston.Transport)
|
|
29
25
|
|
|
30
26
|
//
|
|
31
27
|
// Expose the name of this Transport on the prototype
|
|
32
28
|
//
|
|
33
|
-
MailerLogger.prototype.name =
|
|
34
|
-
|
|
29
|
+
MailerLogger.prototype.name = "MailerLogger"
|
|
35
30
|
|
|
36
31
|
MailerLogger.prototype.log = function (info, callback) {
|
|
37
|
-
let
|
|
38
|
-
|
|
39
|
-
let title = ''
|
|
40
|
-
let body = ''
|
|
41
|
-
|
|
32
|
+
let title = ""
|
|
42
33
|
try {
|
|
43
34
|
try {
|
|
44
|
-
title= info.message.split("\n")[0]
|
|
45
|
-
} catch(
|
|
46
|
-
title= info.message.toString()
|
|
35
|
+
title = info.message.split("\n")[0]
|
|
36
|
+
} catch (_) {
|
|
37
|
+
title = info.message.toString()
|
|
47
38
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
} catch(err) {
|
|
39
|
+
title = uncolor(title)
|
|
40
|
+
} catch (err) {
|
|
52
41
|
title = `Could not create a title for the error (${err.toString()})`
|
|
53
42
|
}
|
|
54
43
|
|
|
44
|
+
let body = info?.message || ""
|
|
55
45
|
try {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
} catch(err) {
|
|
62
|
-
body = `Could not create a body for the error (${err.toString()})`
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
let subject = `${config?.name} [${info.level.toUpperCase()}] ${title}`
|
|
46
|
+
body = uncolor(body)
|
|
47
|
+
} catch (_) {}
|
|
48
|
+
|
|
49
|
+
const html = buildErrorEmailBody(info)
|
|
67
50
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
51
|
+
try {
|
|
52
|
+
const subject = `${config?.name} [${info.level.toUpperCase()}] ${title}`
|
|
53
|
+
|
|
54
|
+
const mail = {
|
|
55
|
+
from: this.from,
|
|
56
|
+
to: this.to,
|
|
57
|
+
subject: subject,
|
|
58
|
+
text: body,
|
|
59
|
+
html: html
|
|
73
60
|
}
|
|
74
61
|
|
|
75
62
|
emailer.queue_email(mail)
|
|
76
|
-
|
|
63
|
+
this.emit("logged")
|
|
77
64
|
callback(null, true)
|
|
78
|
-
|
|
79
|
-
} catch(error) {
|
|
65
|
+
} catch (_) {
|
|
80
66
|
// TODO - How to exxpose info from here
|
|
81
|
-
|
|
82
|
-
callback(null, true)
|
|
67
|
+
this.emit("logged")
|
|
68
|
+
callback(null, true)
|
|
83
69
|
}
|
|
84
|
-
}
|
|
85
|
-
|
|
70
|
+
}
|
|
71
|
+
|
|
86
72
|
return MailerLogger
|
|
87
73
|
}
|
|
88
74
|
|
|
89
|
-
export {init_logger_to_mail}
|
|
75
|
+
export { init_logger_to_mail }
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* https://gist.github.com/suprememoocow/5133080
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* https://github.com/winstonjs/winston/issues/943
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* A function for reopening a winston File logging transport post logrotation on a HUP signal.
|
|
7
|
-
* To send a HUP to your node service, use the postrotate configuration option from logrotate.
|
|
7
|
+
* To send a HUP to your node service, use the postrotate configuration option from logrotate.
|
|
8
8
|
* `postrotate kill -HUP ‘cat /var/run/mynodeservice.pid‘`
|
|
9
9
|
*/
|
|
10
|
-
import path from 'path'
|
|
11
|
-
import fs from 'fs'
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
import fs from "node:fs"
|
|
12
|
+
import path from "node:path"
|
|
15
13
|
|
|
14
|
+
export function reopenTransportOnHupSignal(fileTransport) {
|
|
15
|
+
process.on("SIGHUP", () => {
|
|
16
16
|
const fullname = path.join(fileTransport.dirname, fileTransport._getFile(false))
|
|
17
17
|
|
|
18
18
|
//console.log(`[miolo][file-logger] SIGHUP received. Check if we need to re-open log file ${fullname}...`)
|
|
@@ -25,21 +25,21 @@ export function reopenTransportOnHupSignal(fileTransport) {
|
|
|
25
25
|
fileTransport._stream.destroy() // Soon()
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
const stream = fs.createWriteStream(fullname, fileTransport.options)
|
|
29
29
|
stream.setMaxListeners(Infinity)
|
|
30
30
|
|
|
31
31
|
fileTransport._size = 0
|
|
32
32
|
fileTransport._stream = stream
|
|
33
33
|
|
|
34
34
|
//fileTransport.once('flush', function () {
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
fileTransport.opening = false
|
|
36
|
+
fileTransport.emit("open", fullname)
|
|
37
37
|
//})
|
|
38
38
|
|
|
39
39
|
//fileTransport.flush()
|
|
40
40
|
|
|
41
41
|
console.log(`[miolo][file-logger] Reopened ${fullname} successfully`)
|
|
42
|
-
} catch(error) {
|
|
42
|
+
} catch (error) {
|
|
43
43
|
console.error(`[miolo][file-logger] Error reopening ${fullname}: ${error.toString()}`)
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -52,6 +52,5 @@ export function reopenTransportOnHupSignal(fileTransport) {
|
|
|
52
52
|
})
|
|
53
53
|
*/
|
|
54
54
|
return reopen()
|
|
55
|
-
|
|
56
55
|
})
|
|
57
|
-
}
|
|
56
|
+
}
|
|
@@ -1,126 +1,143 @@
|
|
|
1
1
|
export default class Parser {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
if ((v==null) || (v==undefined)) {
|
|
2
|
+
parse_value_str(v, required = false, def = undefined) {
|
|
3
|
+
if (v === null || v === undefined) {
|
|
5
4
|
if (required) {
|
|
6
|
-
|
|
5
|
+
throw `Wrong str value passed: ${v}`
|
|
7
6
|
}
|
|
8
7
|
return def
|
|
9
8
|
}
|
|
10
|
-
|
|
9
|
+
|
|
11
10
|
return v.toString()
|
|
12
11
|
}
|
|
13
|
-
|
|
14
|
-
parse_field_str(fields, name, required= false, def= undefined) {
|
|
15
|
-
if (
|
|
12
|
+
|
|
13
|
+
parse_field_str(fields, name, required = false, def = undefined) {
|
|
14
|
+
if (!(name in fields)) {
|
|
16
15
|
if (required) {
|
|
17
|
-
|
|
16
|
+
throw `Expected str value not passed for ${name}`
|
|
18
17
|
}
|
|
19
18
|
return def
|
|
20
19
|
}
|
|
21
|
-
|
|
20
|
+
try {
|
|
21
|
+
return this.parse_value_str(fields[name], required, def)
|
|
22
|
+
} catch (e) {
|
|
23
|
+
throw `parse_field_str() Error for ${name}: ${e}`
|
|
24
|
+
}
|
|
22
25
|
}
|
|
23
|
-
|
|
24
|
-
parse_value_int(v, required= false, def= undefined) {
|
|
25
|
-
|
|
26
|
-
if (
|
|
26
|
+
|
|
27
|
+
parse_value_int(v, required = false, def = undefined) {
|
|
28
|
+
const vi = parseInt(v, 10)
|
|
29
|
+
if (vi === null || Number.isNaN(Number(vi))) {
|
|
27
30
|
if (required) {
|
|
28
|
-
|
|
31
|
+
throw `Wrong int value passed: ${v}`
|
|
29
32
|
}
|
|
30
33
|
return def
|
|
31
34
|
}
|
|
32
|
-
|
|
35
|
+
|
|
33
36
|
return vi
|
|
34
37
|
}
|
|
35
|
-
|
|
36
|
-
parse_field_int(fields, name, required= false, def= undefined) {
|
|
37
|
-
if (
|
|
38
|
+
|
|
39
|
+
parse_field_int(fields, name, required = false, def = undefined) {
|
|
40
|
+
if (!(name in fields)) {
|
|
38
41
|
if (required) {
|
|
39
|
-
|
|
42
|
+
throw `Expected int value not passed for ${name}`
|
|
40
43
|
}
|
|
41
44
|
return def
|
|
42
45
|
}
|
|
43
|
-
|
|
46
|
+
try {
|
|
47
|
+
return this.parse_value_int(fields[name], required, def)
|
|
48
|
+
} catch (e) {
|
|
49
|
+
throw `parse_field_int() Error for ${name}: ${e}`
|
|
50
|
+
}
|
|
44
51
|
}
|
|
45
|
-
|
|
46
|
-
parse_value_float(v, required= false, def= undefined) {
|
|
47
|
-
const vf= parseFloat(v)
|
|
48
|
-
if (
|
|
52
|
+
|
|
53
|
+
parse_value_float(v, required = false, def = undefined) {
|
|
54
|
+
const vf = parseFloat(v)
|
|
55
|
+
if (vf === null || Number.isNaN(Number(vf))) {
|
|
49
56
|
if (required) {
|
|
50
|
-
|
|
57
|
+
throw `Wrong float value passed: ${v}`
|
|
51
58
|
}
|
|
52
59
|
return def
|
|
53
60
|
}
|
|
54
|
-
|
|
61
|
+
|
|
55
62
|
return v
|
|
56
63
|
}
|
|
57
|
-
|
|
58
|
-
parse_field_float(fields, name, required= false, def= undefined) {
|
|
59
|
-
if (
|
|
64
|
+
|
|
65
|
+
parse_field_float(fields, name, required = false, def = undefined) {
|
|
66
|
+
if (!(name in fields)) {
|
|
60
67
|
if (required) {
|
|
61
|
-
|
|
68
|
+
throw `Expected float value not passed for ${name}`
|
|
62
69
|
}
|
|
63
70
|
return def
|
|
64
71
|
}
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
try {
|
|
73
|
+
return this.parse_value_float(fields[name], required, def)
|
|
74
|
+
} catch (e) {
|
|
75
|
+
throw `parse_field_float() Error for ${name}: ${e}`
|
|
76
|
+
}
|
|
67
77
|
}
|
|
68
|
-
|
|
69
|
-
parse_value_bool(v, required= false, def= undefined) {
|
|
70
|
-
if (
|
|
78
|
+
|
|
79
|
+
parse_value_bool(v, required = false, def = undefined) {
|
|
80
|
+
if (v === null || v === undefined) {
|
|
71
81
|
if (required) {
|
|
72
|
-
|
|
82
|
+
throw `Wrong bool value passed for ${name}`
|
|
73
83
|
}
|
|
74
84
|
return undefined
|
|
75
85
|
}
|
|
76
|
-
if (
|
|
86
|
+
if (v === true || v === "true" || v === "True" || v === 1 || v === "1") {
|
|
77
87
|
return true
|
|
78
88
|
}
|
|
79
|
-
if (
|
|
89
|
+
if (v === false || v === "false" || v === "False" || v === 0 || v === "0") {
|
|
80
90
|
return false
|
|
81
91
|
}
|
|
82
|
-
|
|
92
|
+
|
|
83
93
|
return def
|
|
84
94
|
}
|
|
85
|
-
|
|
86
|
-
parse_field_bool(fields, name, required= false, def= undefined) {
|
|
87
|
-
if (
|
|
95
|
+
|
|
96
|
+
parse_field_bool(fields, name, required = false, def = undefined) {
|
|
97
|
+
if (!(name in fields)) {
|
|
88
98
|
if (required) {
|
|
89
|
-
|
|
99
|
+
throw `Expected bool value not passed for ${name}`
|
|
90
100
|
}
|
|
91
101
|
return def
|
|
92
102
|
}
|
|
93
|
-
|
|
94
|
-
|
|
103
|
+
try {
|
|
104
|
+
return this.parse_value_bool(fields[name], required, def)
|
|
105
|
+
} catch (e) {
|
|
106
|
+
throw `parse_field_bool() Error for ${name}: ${e}`
|
|
107
|
+
}
|
|
95
108
|
}
|
|
96
|
-
|
|
97
|
-
parse_value_obj(v, required= false, def= undefined) {
|
|
98
|
-
if (
|
|
109
|
+
|
|
110
|
+
parse_value_obj(v, required = false, def = undefined) {
|
|
111
|
+
if (v === null || v === undefined) {
|
|
99
112
|
if (required) {
|
|
100
|
-
|
|
113
|
+
throw `Wrong obj value passed: ${v}`
|
|
101
114
|
}
|
|
102
115
|
return def
|
|
103
116
|
}
|
|
104
|
-
if (Object.keys(v).length
|
|
117
|
+
if (Object.keys(v).length === 0) {
|
|
105
118
|
if (required) {
|
|
106
119
|
throw `Empty obj value passed: ${v}`
|
|
107
120
|
}
|
|
108
121
|
return def
|
|
109
|
-
}
|
|
110
|
-
if (typeof v
|
|
122
|
+
}
|
|
123
|
+
if (typeof v !== "object") {
|
|
111
124
|
throw `Wrong obj value passed: ${v}`
|
|
112
|
-
}
|
|
113
|
-
|
|
125
|
+
}
|
|
126
|
+
|
|
114
127
|
return v
|
|
115
128
|
}
|
|
116
|
-
|
|
117
|
-
parse_field_obj(fields, name, required= false, def= undefined) {
|
|
118
|
-
if (
|
|
129
|
+
|
|
130
|
+
parse_field_obj(fields, name, required = false, def = undefined) {
|
|
131
|
+
if (!(name in fields)) {
|
|
119
132
|
if (required) {
|
|
120
|
-
|
|
133
|
+
throw `Expected obj value not passed for ${name}`
|
|
121
134
|
}
|
|
122
135
|
return def
|
|
123
136
|
}
|
|
124
|
-
|
|
137
|
+
try {
|
|
138
|
+
return this.parse_value_obj(fields[name], required, def)
|
|
139
|
+
} catch (e) {
|
|
140
|
+
throw `parse_field_obj() Error for ${name}: ${e}`
|
|
141
|
+
}
|
|
125
142
|
}
|
|
126
143
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function diffObjs(o1, o2) {
|
|
2
|
+
const missingKeys = []
|
|
3
|
+
|
|
4
|
+
function findMissing(obj1, obj2, prefix = "") {
|
|
5
|
+
for (const key in obj1) {
|
|
6
|
+
if (Object.hasOwn(obj1, key)) {
|
|
7
|
+
const fullKey = prefix ? `${prefix}.${key}` : key
|
|
8
|
+
|
|
9
|
+
if (!obj2 || !(key in obj2)) {
|
|
10
|
+
missingKeys.push(`'${fullKey}'`)
|
|
11
|
+
} else if (
|
|
12
|
+
typeof obj1[key] === "object" &&
|
|
13
|
+
obj1[key] !== null &&
|
|
14
|
+
typeof obj2[key] === "object" &&
|
|
15
|
+
obj2[key] !== null &&
|
|
16
|
+
!Array.isArray(obj1[key]) &&
|
|
17
|
+
!Array.isArray(obj2[key])
|
|
18
|
+
) {
|
|
19
|
+
findMissing(obj1[key], obj2[key], fullKey)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
findMissing(o1, o2)
|
|
26
|
+
|
|
27
|
+
if (missingKeys.length === 0) {
|
|
28
|
+
return ""
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const isPlural = missingKeys.length > 1
|
|
32
|
+
let keysStr = ""
|
|
33
|
+
if (!isPlural) {
|
|
34
|
+
keysStr = missingKeys[0]
|
|
35
|
+
} else {
|
|
36
|
+
const lastKey = missingKeys.pop()
|
|
37
|
+
keysStr = `${missingKeys.join(", ")} and ${lastKey}`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return keysStr
|
|
41
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import Joi from "joi"
|
|
2
|
+
|
|
3
|
+
export function with_miolo_input_schema(fn, schema, options) {
|
|
4
|
+
return async (ctx, params) => {
|
|
5
|
+
let error
|
|
6
|
+
|
|
7
|
+
// Check schema is actually a schema
|
|
8
|
+
if (!schema || !Joi.isSchema(schema)) {
|
|
9
|
+
error = `Expecting input schema for ${fn.name} but something else was found (${typeof schema})`
|
|
10
|
+
ctx.miolo.logger.silly(`[validation][${fn.name}] ${error}`)
|
|
11
|
+
throw new Error(error)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// perform validation
|
|
15
|
+
let v
|
|
16
|
+
try {
|
|
17
|
+
v = schema.validate(params, {
|
|
18
|
+
...(options || {})
|
|
19
|
+
})
|
|
20
|
+
} catch (uerror) {
|
|
21
|
+
error = `Unexpected error validating input data for ${fn.name}: ${uerror?.message || uerror}`
|
|
22
|
+
ctx.miolo.logger.silly(`[validation][${fn.name}] ${error}`)
|
|
23
|
+
throw new Error(error)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// raise validation errors
|
|
27
|
+
if (v?.error) {
|
|
28
|
+
error = `Input schema invalidated data for ${fn.name}: ${v.error}\n${v.error.annotate(true)}`
|
|
29
|
+
ctx.miolo.logger.silly(`[validation][${fn.name}] ${error}`)
|
|
30
|
+
throw new Error(error)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// check parsed value is ok
|
|
34
|
+
if (!v?.value) {
|
|
35
|
+
const description = schema.describe()
|
|
36
|
+
|
|
37
|
+
// Check if schema was deliberately set to allow only null
|
|
38
|
+
// schema = Joi.any().allow(null)
|
|
39
|
+
const isOnlyNull =
|
|
40
|
+
description.type === "any" &&
|
|
41
|
+
description.allow &&
|
|
42
|
+
description.allow.length === 1 &&
|
|
43
|
+
description.allow[0] === null
|
|
44
|
+
|
|
45
|
+
if (!isOnlyNull) {
|
|
46
|
+
error = `Input schema returned unknown result for ${fn.name}: ${JSON.stringify(v)}`
|
|
47
|
+
ctx.miolo.logger.silly(`[validation][${fn.name}] ${error}`)
|
|
48
|
+
throw new Error(error)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return await fn(ctx, v.value)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import Joi from "joi"
|
|
2
|
+
import { diffObjs } from "./diffObjs.mjs"
|
|
3
|
+
|
|
4
|
+
export function with_miolo_output_schema(fn, schema, options) {
|
|
5
|
+
const fnName = fn?.name ? `[${fn.name}]` : ""
|
|
6
|
+
|
|
7
|
+
return async (ctx, params) => {
|
|
8
|
+
let error
|
|
9
|
+
|
|
10
|
+
// Check schema is actually a schema
|
|
11
|
+
if (!schema || !Joi.isSchema(schema)) {
|
|
12
|
+
error = `Expecting output schema for ${fn.name} but something else was found (${typeof schema})`
|
|
13
|
+
ctx.miolo.logger.silly(`[validation]${fnName} ${error}`)
|
|
14
|
+
throw new Error(error)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Call the function first
|
|
18
|
+
const result = await fn(ctx, params)
|
|
19
|
+
|
|
20
|
+
// perform validation over the result
|
|
21
|
+
let v
|
|
22
|
+
try {
|
|
23
|
+
v = schema.validate(result, {
|
|
24
|
+
stripUnknown: true,
|
|
25
|
+
...(options || {})
|
|
26
|
+
})
|
|
27
|
+
} catch (uerror) {
|
|
28
|
+
error = `Unexpected error validating output data for ${fn.name}: ${uerror?.message || uerror}`
|
|
29
|
+
ctx.miolo.logger.silly(`[validation]${fnName} ${error}`)
|
|
30
|
+
throw new Error(error)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// raise validation errors
|
|
34
|
+
if (v?.error) {
|
|
35
|
+
error = `Output schema invalidated data for ${fn.name}: ${v.error}\n${v.error.annotate(true)}`
|
|
36
|
+
ctx.miolo.logger.silly(`[validation]${fnName} ${error}`)
|
|
37
|
+
throw new Error(error)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// check parsed value is ok
|
|
41
|
+
if (!v?.value) {
|
|
42
|
+
const description = schema.describe()
|
|
43
|
+
|
|
44
|
+
// Check if schema was deliberately set to allow only null
|
|
45
|
+
// schema = Joi.any().allow(null)
|
|
46
|
+
const isOnlyNull =
|
|
47
|
+
description.type === "any" &&
|
|
48
|
+
description.allow &&
|
|
49
|
+
description.allow.length === 1 &&
|
|
50
|
+
description.allow[0] === null
|
|
51
|
+
|
|
52
|
+
if (!isOnlyNull) {
|
|
53
|
+
error = `Output schema returned unknown result for ${fn.name}: ${JSON.stringify(v)}`
|
|
54
|
+
ctx.miolo.logger.silly(`[validation]${fnName} ${error}`)
|
|
55
|
+
throw new Error(error)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const diff = diffObjs(result, v.value)
|
|
60
|
+
if (diff) {
|
|
61
|
+
ctx.miolo.logger.debug(`[validation]${fnName} Output schema has removed ${diff}`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return v.value
|
|
65
|
+
}
|
|
66
|
+
}
|