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.
Files changed (246) hide show
  1. package/bin/build/build.mjs +53 -0
  2. package/bin/build/build_bin.mjs +17 -0
  3. package/bin/build/cli/client.mjs +52 -0
  4. package/bin/build/cli/css.mjs +22 -0
  5. package/bin/build/cli/index.mjs +40 -0
  6. package/bin/build/cli/ssr.mjs +21 -0
  7. package/bin/build/server/aliases.mjs +112 -0
  8. package/bin/build/server/babel.config.js +24 -0
  9. package/bin/build/server/banner.mjs +20 -0
  10. package/bin/build/server/bundle.mjs +111 -0
  11. package/bin/build/server/fix.mjs +15 -0
  12. package/bin/build/server/index.mjs +69 -0
  13. package/bin/build/server/options.mjs +83 -0
  14. package/bin/create/auth.mjs +23 -0
  15. package/bin/create/copy.mjs +175 -0
  16. package/bin/create/docker.mjs +25 -0
  17. package/bin/create/index.mjs +137 -0
  18. package/bin/create/pkgjson.mjs +72 -0
  19. package/bin/create/prepare-template.mjs +158 -0
  20. package/bin/create/validation.mjs +27 -0
  21. package/bin/dev/dev.mjs +32 -23
  22. package/bin/dev/dev_start.mjs +6 -5
  23. package/bin/index.mjs +94 -52
  24. package/bin/prod-bin/create-bin.mjs +42 -27
  25. package/bin/prod-bin/run.mjs +13 -9
  26. package/bin/{prod-run → run}/pid.mjs +4 -4
  27. package/bin/run/restart.mjs +13 -0
  28. package/bin/run/start.mjs +18 -0
  29. package/bin/run/stop.mjs +20 -0
  30. package/bin/util.mjs +35 -11
  31. package/package.json +59 -39
  32. package/src/config/.env +34 -12
  33. package/src/config/defaults.mjs +253 -185
  34. package/src/config/env.mjs +40 -22
  35. package/src/config/index.mjs +19 -24
  36. package/src/config/util.mjs +25 -10
  37. package/src/db-conn.mjs +34 -0
  38. package/src/engines/cron/emails.mjs +10 -5
  39. package/src/engines/cron/index.mjs +45 -51
  40. package/src/engines/cron/init.mjs +16 -17
  41. package/src/engines/cron/ipsum.mjs +65 -60
  42. package/src/engines/cron/syscheck.mjs +30 -30
  43. package/src/engines/emailer/index.mjs +1 -2
  44. package/src/engines/emailer/queue.mjs +14 -20
  45. package/src/engines/emailer/transporter.mjs +86 -74
  46. package/src/engines/geoip/index.mjs +23 -28
  47. package/src/engines/http/index.mjs +26 -15
  48. package/src/engines/logger/buildErrorEmailBody.mjs +72 -0
  49. package/src/engines/logger/index.mjs +114 -122
  50. package/src/engines/logger/injectStackTrace.mjs +59 -0
  51. package/src/engines/logger/logger_mail.mjs +47 -61
  52. package/src/engines/logger/reopenTransportOnHupSignal.mjs +12 -13
  53. package/src/engines/parser/Parser.mjs +77 -60
  54. package/src/engines/parser/index.mjs +1 -1
  55. package/src/engines/schema/diffObjs.mjs +41 -0
  56. package/src/engines/schema/index.mjs +4 -0
  57. package/src/engines/schema/input.mjs +54 -0
  58. package/src/engines/schema/output.mjs +66 -0
  59. package/src/engines/socket/index.mjs +44 -46
  60. package/src/index.mjs +15 -10
  61. package/src/middleware/auth/basic.mjs +41 -40
  62. package/src/middleware/auth/custom.mjs +10 -13
  63. package/src/middleware/auth/guest.mjs +27 -27
  64. package/src/middleware/auth/passport/index.mjs +374 -0
  65. package/src/middleware/auth/passport/session/index.mjs +43 -0
  66. package/src/middleware/auth/{credentials → passport}/session/store.mjs +35 -15
  67. package/src/middleware/auth/passport/session/store_koa_redis.mjs +3 -0
  68. package/src/middleware/context/cache/index.mjs +78 -33
  69. package/src/middleware/context/cache/options.mjs +19 -21
  70. package/src/middleware/context/db.mjs +45 -20
  71. package/src/middleware/context/index.mjs +12 -12
  72. package/src/middleware/extra.mjs +4 -5
  73. package/src/middleware/http/body.mjs +25 -25
  74. package/src/middleware/http/catcher.mjs +81 -8
  75. package/src/middleware/http/custom_blacklist.mjs +19 -16
  76. package/src/middleware/http/headers.mjs +37 -34
  77. package/src/middleware/http/ratelimit.mjs +16 -23
  78. package/src/middleware/http/request.mjs +60 -65
  79. package/src/middleware/routes/catch_js_error.mjs +30 -23
  80. package/src/middleware/routes/robots.mjs +4 -7
  81. package/src/middleware/routes/router/crud/attachCrudRoutes.mjs +108 -90
  82. package/src/middleware/routes/router/crud/getCrudConfig.mjs +31 -55
  83. package/src/middleware/routes/router/defaults.mjs +6 -19
  84. package/src/middleware/routes/router/index.mjs +17 -21
  85. package/src/middleware/routes/router/queries/attachQueriesRoutes.mjs +227 -50
  86. package/src/middleware/routes/router/queries/getQueriesConfig.mjs +45 -55
  87. package/src/middleware/routes/router/utils.mjs +41 -26
  88. package/src/middleware/ssr/context.mjs +5 -7
  89. package/src/middleware/ssr/html.mjs +66 -43
  90. package/src/middleware/ssr/loader.mjs +11 -14
  91. package/src/middleware/ssr/ssr_render.mjs +39 -22
  92. package/src/middleware/static/index.mjs +33 -14
  93. package/src/middleware/vite/devserver.mjs +38 -22
  94. package/src/middleware/vite/watcher.mjs +12 -14
  95. package/src/server-cron.mjs +13 -8
  96. package/src/server-dev.mjs +13 -16
  97. package/src/server.mjs +49 -51
  98. package/template/.agent/skills/miolo-app-arch/SKILL.md +218 -0
  99. package/template/.agent/skills/miolo-auth/SKILL.md +450 -0
  100. package/template/.agent/skills/miolo-cli-router/SKILL.md +394 -0
  101. package/template/.agent/skills/miolo-database/SKILL.md +358 -0
  102. package/template/.agent/skills/miolo-react-patterns/SKILL.md +426 -0
  103. package/template/.agent/skills/miolo-routing/SKILL.md +326 -0
  104. package/template/.agent/skills/miolo-schemas/SKILL.md +329 -0
  105. package/template/.agent/skills/miolo-session-context/SKILL.md +397 -0
  106. package/template/.agent/skills/miolo-ssr/SKILL.md +433 -0
  107. package/template/.editorconfig +18 -0
  108. package/template/.env +120 -0
  109. package/template/biome.json +63 -0
  110. package/template/components.json +21 -0
  111. package/template/db/init.sh +89 -0
  112. package/template/db/sql/00_drop.sql +2 -0
  113. package/template/db/sql/01_users.sql +31 -0
  114. package/template/db/sql/02_todos.sql +20 -0
  115. package/template/docker/Dockerfile +13 -0
  116. package/template/docker/docker-compose.yaml +79 -0
  117. package/template/gitignore +42 -0
  118. package/template/jsconfig.json +18 -0
  119. package/template/package.json +88 -0
  120. package/template/postcss.config.js +9 -0
  121. package/template/src/cli/App.jsx +25 -0
  122. package/template/src/cli/components/JsonTreeViewer.jsx +128 -0
  123. package/template/src/cli/components/shadcn-io/spinner/index.jsx +232 -0
  124. package/template/src/cli/components/stepper.jsx +408 -0
  125. package/template/src/cli/components/ui/avatar.jsx +36 -0
  126. package/template/src/cli/components/ui/badge.jsx +31 -0
  127. package/template/src/cli/components/ui/breadcrumb.jsx +97 -0
  128. package/template/src/cli/components/ui/card.jsx +73 -0
  129. package/template/src/cli/components/ui/collapsible.jsx +16 -0
  130. package/template/src/cli/components/ui/dropdown-menu.jsx +179 -0
  131. package/template/src/cli/components/ui/field.jsx +217 -0
  132. package/template/src/cli/components/ui/input.jsx +19 -0
  133. package/template/src/cli/components/ui/label.jsx +17 -0
  134. package/template/src/cli/components/ui/pagination.jsx +99 -0
  135. package/template/src/cli/components/ui/patched/alert.jsx +56 -0
  136. package/template/src/cli/components/ui/patched/button.jsx +45 -0
  137. package/template/src/cli/components/ui/patched/dialog.jsx +114 -0
  138. package/template/src/cli/components/ui/patched/sidebar.jsx +660 -0
  139. package/template/src/cli/components/ui/select.jsx +141 -0
  140. package/template/src/cli/components/ui/separator.jsx +21 -0
  141. package/template/src/cli/components/ui/sheet.jsx +115 -0
  142. package/template/src/cli/components/ui/skeleton.jsx +13 -0
  143. package/template/src/cli/components/ui/sonner.jsx +22 -0
  144. package/template/src/cli/components/ui/switch.jsx +25 -0
  145. package/template/src/cli/components/ui/table.jsx +88 -0
  146. package/template/src/cli/components/ui/textarea.jsx +16 -0
  147. package/template/src/cli/components/ui/tooltip.jsx +45 -0
  148. package/template/src/cli/config/store_keys.mjs +2 -0
  149. package/template/src/cli/context/data/DataContext.jsx +5 -0
  150. package/template/src/cli/context/data/DataProvider.jsx +44 -0
  151. package/template/src/cli/context/data/useBreads.mjs +15 -0
  152. package/template/src/cli/context/data/useDataContext.mjs +4 -0
  153. package/template/src/cli/context/session/SessionContext.mjs +4 -0
  154. package/template/src/cli/context/session/SessionProvider.jsx +31 -0
  155. package/template/src/cli/context/session/makePermissioner.mjs +34 -0
  156. package/template/src/cli/context/session/useSessionContext.mjs +6 -0
  157. package/template/src/cli/context/theme/ThemeContext.mjs +4 -0
  158. package/template/src/cli/context/theme/ThemeProvider.jsx +49 -0
  159. package/template/src/cli/context/theme/useThemeContext.mjs +6 -0
  160. package/template/src/cli/context/ui/UIContext.jsx +5 -0
  161. package/template/src/cli/context/ui/UIProvider.jsx +16 -0
  162. package/template/src/cli/context/ui/useUIContext.mjs +4 -0
  163. package/template/src/cli/context/util.mjs +17 -0
  164. package/template/src/cli/entry-cli.jsx +33 -0
  165. package/template/src/cli/hooks/useIsMobile.mjs +19 -0
  166. package/template/src/cli/hooks/useStoragedState.mjs +63 -0
  167. package/template/src/cli/index.html +29 -0
  168. package/template/src/cli/layout/app-sidebar.jsx +25 -0
  169. package/template/src/cli/layout/main-layout.jsx +63 -0
  170. package/template/src/cli/layout/nav-last-todos.jsx +72 -0
  171. package/template/src/cli/layout/nav-main.jsx +39 -0
  172. package/template/src/cli/layout/nav-user.jsx +105 -0
  173. package/template/src/cli/layout/prop-switcher.jsx +93 -0
  174. package/template/src/cli/lib/utils.mjs +10 -0
  175. package/template/src/cli/pages/Index.jsx +13 -0
  176. package/template/src/cli/pages/IndexOffline.jsx +13 -0
  177. package/template/src/cli/pages/IndexOnline.jsx +18 -0
  178. package/template/src/cli/pages/dash/Dashboard.jsx +29 -0
  179. package/template/src/cli/pages/offline/Login.jsx +43 -0
  180. package/template/src/cli/pages/offline/LoginForm.jsx +115 -0
  181. package/template/src/cli/pages/security/Security.jsx +39 -0
  182. package/template/src/cli/pages/security/SecurityForm.jsx +106 -0
  183. package/template/src/cli/pages/todos/TodoActions.jsx +99 -0
  184. package/template/src/cli/pages/todos/TodoAdd.jsx +43 -0
  185. package/template/src/cli/pages/todos/TodoList.jsx +60 -0
  186. package/template/src/cli/pages/todos/Todos.jsx +23 -0
  187. package/template/src/cli/pages/todos/context/TodosContext.jsx +5 -0
  188. package/template/src/cli/pages/todos/context/TodosProvider.jsx +191 -0
  189. package/template/src/cli/pages/todos/context/useTodosContext.mjs +4 -0
  190. package/template/src/ns/models/Todo.mjs +29 -0
  191. package/template/src/ns/models/TodoList.mjs +8 -0
  192. package/template/src/ns/models/User.mjs +40 -0
  193. package/template/src/server/bot/check_today.mjs +10 -0
  194. package/template/src/server/io/cache/base.mjs +21 -0
  195. package/template/src/server/io/db/filter.mjs +92 -0
  196. package/template/src/server/io/db/todos/delete.mjs +29 -0
  197. package/template/src/server/io/db/todos/find.mjs +13 -0
  198. package/template/src/server/io/db/todos/read.mjs +83 -0
  199. package/template/src/server/io/db/todos/toggle.mjs +37 -0
  200. package/template/src/server/io/db/todos/upsave.mjs +32 -0
  201. package/template/src/server/io/db/triggers/user.mjs +13 -0
  202. package/template/src/server/io/db/users/auth.mjs +132 -0
  203. package/template/src/server/io/db/users/pwd.mjs +38 -0
  204. package/template/src/server/io/db/users/save.mjs +17 -0
  205. package/template/src/server/miolo/auth/basic.mjs +15 -0
  206. package/template/src/server/miolo/auth/guest.mjs +3 -0
  207. package/template/src/server/miolo/auth/passport.mjs +73 -0
  208. package/template/src/server/miolo/cache.mjs +11 -0
  209. package/template/src/server/miolo/cron/foo.mjs +7 -0
  210. package/template/src/server/miolo/cron/index.mjs +28 -0
  211. package/template/src/server/miolo/cron/invalidate.mjs +21 -0
  212. package/template/src/server/miolo/db.mjs +36 -0
  213. package/template/src/server/miolo/http.mjs +14 -0
  214. package/template/src/server/miolo/index.mjs +43 -0
  215. package/template/src/server/miolo/routes/crud.mjs +16 -0
  216. package/template/src/server/miolo/routes/index.mjs +8 -0
  217. package/template/src/server/miolo/ssr/entry-server.jsx +13 -0
  218. package/template/src/server/miolo/ssr/loader.mjs +18 -0
  219. package/template/src/server/routes/index.mjs +66 -0
  220. package/template/src/server/routes/todos/mod.mjs +52 -0
  221. package/template/src/server/routes/todos/read.mjs +45 -0
  222. package/template/src/server/routes/todos/special.mjs +47 -0
  223. package/template/src/server/routes/users/user.mjs +54 -0
  224. package/template/src/server/server.mjs +10 -0
  225. package/template/src/server/utils/crypt.mjs +38 -0
  226. package/template/src/server/utils/io.mjs +15 -0
  227. package/template/src/server/utils/pwdfor.mjs +25 -0
  228. package/template/src/server/utils/schema.mjs +22 -0
  229. package/template/src/static/img/default/profile.png +0 -0
  230. package/template/src/static/img/favicon.ico +0 -0
  231. package/template/src/static/img/miolo_logo.png +0 -0
  232. package/template/src/static/img/miolo_name.png +0 -0
  233. package/template/src/static/public/manifest.json +21 -0
  234. package/template/src/static/public/sw.js +79 -0
  235. package/template/src/static/style/globals.css +156 -0
  236. package/template/src/static/style/json-tree.css +54 -0
  237. package/template/src/static/style/skeleton.css +49 -0
  238. package/bin/prod-build/build-client.mjs +0 -67
  239. package/bin/prod-build/build-server.mjs +0 -58
  240. package/bin/prod-run/restart.mjs +0 -9
  241. package/bin/prod-run/start.mjs +0 -15
  242. package/bin/prod-run/stop.mjs +0 -20
  243. package/src/engines/logger/verify.mjs +0 -22
  244. package/src/middleware/auth/credentials/index.mjs +0 -151
  245. package/src/middleware/auth/credentials/session/index.mjs +0 -24
  246. package/src/middleware/auth/credentials/session/store_koa_redis.mjs +0 -3
@@ -1,66 +1,61 @@
1
- import fs from 'fs'
2
- import {Reader} from '@maxmind/geoip2-node'
1
+ import fs from "node:fs"
2
+ import { Reader } from "@maxmind/geoip2-node"
3
3
 
4
- let _geoip_reader = undefined
4
+ let _geoip_reader
5
5
 
6
- const _geoip_def_local_ips= [
7
- '127.0.0.1',
8
- '::1:',
9
- '172.19.0.1' // Docker inner - probably healthcheck
6
+ const _geoip_def_local_ips = [
7
+ "127.0.0.1",
8
+ "::1:",
9
+ "172.19.0.1" // Docker inner - probably healthcheck
10
10
  ]
11
11
 
12
12
  function _geoip_is_local(ip, local_ips = []) {
13
13
  // if (process.env.NODE_ENV == 'development') {
14
14
  // return true
15
15
  // }
16
-
17
- const all_local_ips = [
18
- ..._geoip_def_local_ips,
19
- ...local_ips || []
20
- ]
16
+
17
+ const all_local_ips = [..._geoip_def_local_ips, ...(local_ips || [])]
21
18
  return all_local_ips.indexOf(ip) >= 0
22
19
  }
23
20
 
24
-
25
-
26
- function _geoip_init(db = '/var/lib/GeoIP/GeoLite2-City.mmdb', logger= console) {
21
+ function _geoip_init(db = "/var/lib/GeoIP/GeoLite2-City.mmdb", logger = console) {
27
22
  try {
28
- if (_geoip_reader != undefined) {
23
+ if (_geoip_reader !== undefined) {
29
24
  return _geoip_reader
30
25
  }
31
26
  const dbBuffer = fs.readFileSync(db)
32
27
  _geoip_reader = Reader.openBuffer(dbBuffer)
33
28
  return _geoip_reader
34
- } catch(error) {
29
+ } catch (error) {
35
30
  logger.error(`[geoip] Error initing:`)
36
31
  logger.error(error)
37
32
  return undefined
38
33
  }
39
34
  }
40
35
 
41
- export const geoip_localize_ip= (ip, config, logger= console) => {
36
+ export const geoip_localize_ip = (ip, config, logger = console) => {
42
37
  if (_geoip_is_local(ip, config?.local_ips)) {
43
38
  return {
44
39
  local: true,
45
- country: '',
46
- city: '',
47
- }
40
+ country: "",
41
+ city: ""
42
+ }
48
43
  }
49
44
 
50
45
  try {
51
46
  const reader = _geoip_init(config?.db, logger)
52
- const resp= reader.city(ip)
47
+ const resp = reader.city(ip)
53
48
  return {
54
49
  country: resp.country.isoCode,
55
- city : resp.city?.names?.en
50
+ city: resp.city?.names?.en
56
51
  }
57
- } catch(error) {
52
+ } catch (_) {
58
53
  logger.error(`[geoip] Error localizing IP ${ip} (not in the database)`)
59
- //logger.error(error)
54
+ //logger.error(_)
60
55
  }
61
56
 
62
57
  return {
63
- country: '',
64
- city: '',
65
- }
58
+ country: "",
59
+ city: ""
60
+ }
66
61
  }
@@ -1,7 +1,7 @@
1
- import http from 'http'
1
+ import http from "node:http"
2
+ import https from "node:https"
2
3
  //import util from 'util'
3
- import { createHttpTerminator } from 'http-terminator'
4
-
4
+ import { createHttpTerminator } from "http-terminator"
5
5
 
6
6
  // promisify the server.listen()
7
7
 
@@ -25,30 +25,41 @@ const _listenAsync = (server, port, hostname) => {
25
25
 
26
26
  export function init_http_server(app, config) {
27
27
  const miolo = app.context.miolo
28
- const logger= miolo.logger
28
+ const logger = miolo.logger
29
29
 
30
30
  const _http_start = async () => {
31
31
  try {
32
32
  // If previous server already created, lets avoid
33
- if (app.http?.server != undefined) {
34
- logger.warn(`[http][start] Server already running on ${app?.http?.hostname}:${app?.http?.port}`)
33
+ if (app.http?.server !== undefined) {
34
+ logger.warn(
35
+ `[http][start] Server already running on ${app?.http?.hostname}:${app?.http?.port}`
36
+ )
35
37
  return
36
38
  }
37
39
 
38
40
  // Init server
39
- const server = http.createServer(app.callback())
41
+ const server =
42
+ config?.ssl === undefined
43
+ ? http.createServer(app.callback())
44
+ : https
45
+ .createServer(config.ssl, app.callback())
46
+ .listen(config.port, config.hostname, () => {
47
+ logger.info("[http][start] Server running at https://localhost:8010")
48
+ })
40
49
 
41
50
  // Init terminator
42
51
  const httpTerminator = createHttpTerminator({
43
- server,
52
+ server
44
53
  })
45
54
 
46
55
  const http_stop = async () => {
47
56
  try {
48
57
  await httpTerminator.terminate()
49
58
  delete app.http.server
50
- logger.info(`[http][stop] miolo has been shutdowned from ${config.hostname}:${config.port}`)
51
- } catch(error) {
59
+ logger.info(
60
+ `[http][stop] miolo has been shutdowned from ${config.hostname}:${config.port}`
61
+ )
62
+ } catch (error) {
52
63
  logger.error(`[http][stop] error: ${error}`)
53
64
  }
54
65
  }
@@ -59,21 +70,21 @@ export function init_http_server(app, config) {
59
70
  // Finally start the server
60
71
  await _listenAsync(server, config.port, config.hostname)
61
72
  logger.info(`[http][start] miolo is listening on ${config.hostname}:${config.port}`)
62
- } catch(error) {
73
+ } catch (error) {
63
74
  logger.error(`[http][start] error: ${error}`)
64
75
  }
65
- }
76
+ }
66
77
 
67
78
  // Attach objects to app
68
79
  app.http = {
69
80
  server: undefined,
70
81
  start: _http_start,
71
82
  stop: async () => {
72
- logger.warn(`[http] stop() stop() function still not attached. Is server running?`)
83
+ logger.warn(`[http] stop() function still not attached. Is server running?`)
73
84
  },
74
85
  hostname: config.hostname,
75
86
  port: config.port
76
- }
87
+ }
77
88
 
78
89
  return app
79
- }
90
+ }
@@ -0,0 +1,72 @@
1
+ import os from "node:os"
2
+
3
+ /**
4
+ * Genera un cuerpo de email HTML a partir del objeto info de Winston.
5
+ * @param {Object} info - El objeto info transformado por Winston.
6
+ * @returns {String} HTML string.
7
+ */
8
+ export default function buildErrorEmailBody(info) {
9
+ // 1. Extraemos propiedades estándar y conocidas
10
+ const { timestamp, level, message, stack, ...metadata } = info
11
+
12
+ // 2. Preparamos el Stack Trace (si existe)
13
+ // Nota: Winston solo tiene 'stack' si usas format.errors({ stack: true })
14
+ const stackHtml = stack
15
+ ? `<div style="margin-top: 20px;">
16
+ <h3 style="color: #d9534f;">Stack Trace</h3>
17
+ <pre style="background: #f4f4f4; padding: 10px; border: 1px solid #ddd; overflow-x: auto; font-size: 12px; font-family: monospace;">${stack}</pre>
18
+ </div>`
19
+ : "<p><em>No stack trace available (Log was likely a string or logical error).</em></p>"
20
+
21
+ // 3. Preparamos los Metadatos (el resto de propiedades en info)
22
+ // Esto es vital en Koa para ver requestId, url, method, etc.
23
+ const metaRows = Object.entries(metadata)
24
+ .map(([key, value]) => {
25
+ // Si el valor es un objeto complejo, lo pasamos a string
26
+ const displayValue =
27
+ typeof value === "object"
28
+ ? `<pre style="margin: 0;">${JSON.stringify(value, null, 2)}</pre>`
29
+ : value
30
+
31
+ return `
32
+ <tr>
33
+ <td style="padding: 8px; border-bottom: 1px solid #ddd; font-weight: bold; width: 30%;">${key}</td>
34
+ <td style="padding: 8px; border-bottom: 1px solid #ddd;">${displayValue}</td>
35
+ </tr>
36
+ `
37
+ })
38
+ .join("")
39
+
40
+ const metadataHtml =
41
+ metaRows.length > 0
42
+ ? `<div style="margin-top: 20px;">
43
+ <h3>Metadata / Context</h3>
44
+ <table style="width: 100%; border-collapse: collapse; font-family: sans-serif; font-size: 14px;">
45
+ ${metaRows}
46
+ </table>
47
+ </div>`
48
+ : ""
49
+
50
+ // 4. Construcción final del HTML
51
+ return `
52
+ <div style="font-family: Arial, sans-serif; color: #333; line-height: 1.6;">
53
+ <h2 style="background-color: #ca7d7bff; color: white; padding: 10px;">🚨 Error at [${process.env.MIOLO_NAME || "miolo"}]</h2>
54
+
55
+ <div style="padding: 10px; border: 1px solid #ddd; background-color: #fff9f9;">
56
+ <strong>Message:</strong> <span style="font-size: 1.2em;">${message
57
+ .split("\n")
58
+ .map((l) => `<div>${l}</div>`)
59
+ .join("\n")}</span><br>
60
+ <strong>Time:</strong> ${timestamp || new Date().toISOString()}<br>
61
+ <strong>Server:</strong> ${os.hostname()}
62
+ </div>
63
+
64
+ ${stackHtml}
65
+ ${metadataHtml}
66
+
67
+ <div style="margin-top: 30px; font-size: 12px; color: #777; border-top: 1px solid #eee; padding-top: 10px;">
68
+ Sent automatically by <strong>miolo</strong> logger.
69
+ </div>
70
+ </div>
71
+ `
72
+ }
@@ -1,67 +1,66 @@
1
1
  // import fs from 'fs'
2
2
  // import path from 'path'
3
- import { red, cyan, magenta, yellow, gray, red_light } from 'tinguir'
3
+ import { blue, blue_light, cyan, magenta, red, yellow } from "tinguir"
4
+ import { createLogger, format, transports } from "winston"
5
+ import injectStackTrace from "./injectStackTrace.mjs"
4
6
  /* https://github.com/winstonjs/winston/issues/925 */
5
7
  /* https://github.com/winstonjs/winston/issues/287 */
6
- import {init_logger_to_mail} from './logger_mail.mjs'
7
- import { createLogger, format, transports } from 'winston'
8
- import { reopenTransportOnHupSignal } from './reopenTransportOnHupSignal.mjs'
8
+ import { init_logger_to_mail } from "./logger_mail.mjs"
9
+ import { reopenTransportOnHupSignal } from "./reopenTransportOnHupSignal.mjs"
10
+
9
11
  // import 'winston-daily-rotate-file'
10
12
  // import { /*intre_to_str,*/ intre_now } from 'intre'
11
13
 
12
14
  const { combine, timestamp, _label, printf, errors } = format
13
15
 
14
-
15
-
16
- const init_logger = (config, emailer, prefix= 'miolo') => {
17
- const LEVEL_COLORS= {
18
- silly : gray,
19
- debug : magenta,
16
+ const init_logger = (config, emailer, prefix = "miolo") => {
17
+ const LEVEL_COLORS = {
18
+ silly: blue_light,
19
+ debug: blue,
20
20
  verbose: cyan,
21
- info : yellow,
22
- warn : red_light,
23
- error : red
21
+ info: magenta,
22
+ warn: yellow,
23
+ error: red
24
24
  }
25
25
 
26
- const LEVEL_ABBRV= {
27
- silly : 'sly',
28
- debug : 'dbg',
29
- verbose: 'vbs',
30
- info : 'inf',
31
- warn : 'wrn',
32
- error : 'err',
26
+ const LEVEL_ABBRV = {
27
+ silly: "sly",
28
+ debug: "dbg",
29
+ verbose: "vbs",
30
+ info: "inf",
31
+ warn: "wrn",
32
+ error: "err"
33
33
  }
34
34
 
35
35
  const _INDENT_SIZE = 4
36
36
  let _INDENT = 0
37
- let _SECTIONS = {}
37
+ const _SECTIONS = {}
38
38
 
39
- const myFormat = info => {
39
+ const myFormat = (info) => {
40
40
  const lc = LEVEL_COLORS[info.level]
41
41
  const tm = new Date(info.timestamp)
42
- const ts= tm.toLocaleString(config?.format?.locale || 'en')
43
- let sindent = ''.padStart(_INDENT, ' ')
42
+ const ts = tm.toLocaleString(config?.format?.locale || "en")
43
+ const sindent = "".padStart(_INDENT, " ")
44
44
  //const ts= tm.toString().substr(4, 20)
45
- const log= `[${prefix}] ${lc(ts)} ${lc(LEVEL_ABBRV[info.level])} ${sindent}${info.message}`
46
- return info.stack
47
- ? `${log}\n${info.stack}`
48
- : log
45
+ const log = `[${prefix}] ${lc(ts)} ${lc(LEVEL_ABBRV[info.level])} ${sindent}${info.message}`
46
+ return info.stack ? `${log}\n${info.stack}` : log
49
47
  }
50
48
 
51
- let _log_transports= []
49
+ const _log_transports = []
52
50
  //
53
51
  // Console transport
54
52
  // If we're not in production then log to the `console` with the format:
55
53
  // `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
56
- //
54
+ //
57
55
 
58
56
  if (config?.console?.enabled === true) {
59
57
  _log_transports.push(
60
- new transports.Console({
61
- humanReadableUnhandledException: true,
62
- level : config?.console?.level || config?.level || 'silly',
63
- handleExceptions: true
64
- }))
58
+ new transports.Console({
59
+ humanReadableUnhandledException: true,
60
+ level: config?.console?.level || config?.level || "silly",
61
+ handleExceptions: true
62
+ })
63
+ )
65
64
  }
66
65
 
67
66
  //
@@ -72,62 +71,60 @@ const init_logger = (config, emailer, prefix= 'miolo') => {
72
71
  // https://gist.github.com/suprememoocow/5133080
73
72
  //
74
73
  if (config?.file?.enabled === true) {
75
-
76
- const fileTransport = new transports.File({
77
- filename : config?.file?.filename
78
- ? config.file.filename.replace('%MIOLO%', prefix)
79
- : '/var/log/afialapis/miolo.log',
80
- level : config?.file?.level || config?.level || 'info' ,
74
+ const fileTransport = new transports.File({
75
+ filename: config?.file?.filename
76
+ ? config.file.filename.replace("%MIOLO%", prefix)
77
+ : "/var/log/afialapis/miolo.log",
78
+ level: config?.file?.level || config?.level || "info",
81
79
  humanReadableUnhandledException: true,
82
80
  handleExceptions: true,
83
81
  maxRetries: 10
84
82
  })
85
-
83
+
86
84
  if (config?.file?.hup_patch === true) {
87
85
  reopenTransportOnHupSignal(fileTransport)
88
86
  }
89
87
 
90
88
  _log_transports.push(fileTransport)
91
89
 
92
-
93
- // const _file_log = (s) => {
94
- // const filename = path.join(fileTransport.dirname, fileTransport.filename)
95
- //
96
- // // console.log(fileTransport._stream )
97
- // const msg = myFormat({
98
- // level: 'info',
99
- // message: `[logger][file-rotate] ${s}\n`,
100
- // timestamp: intre_now()
101
- // })
102
- //
103
- // try {
104
- // fs.accessSync(filename, fs.constants.F_OK)
105
- // fs.appendFileSync(filename, msg)
106
- // } catch(_) {
107
- // fs.writeFileSync(filename, msg, { encoding: 'utf-8' })
108
- // }
109
- // if (config?.console?.enabled === true) {
110
- // if (fileTransport.levels[config?.console?.level || config?.level || 'error'] >= fileTransport.levels['info']) {
111
- // console.log(msg)
112
- // }
113
- // }
114
- // }
115
- //
116
- // fileTransport.on('finish', function(info) {
117
- // // do something fun
118
- // _file_log('Log done')
119
- // })
120
- // fileTransport.on('error', function(error) {
121
- // // do something fun
122
- // _file_log(red(`Error: ${error}`))
123
- // })
124
- //
90
+ // const _file_log = (s) => {
91
+ // const filename = path.join(fileTransport.dirname, fileTransport.filename)
92
+ //
93
+ // // console.log(fileTransport._stream )
94
+ // const msg = myFormat({
95
+ // level: 'info',
96
+ // message: `[logger][file-rotate] ${s}\n`,
97
+ // timestamp: intre_now()
98
+ // })
99
+ //
100
+ // try {
101
+ // fs.accessSync(filename, fs.constants.F_OK)
102
+ // fs.appendFileSync(filename, msg)
103
+ // } catch(_) {
104
+ // fs.writeFileSync(filename, msg, { encoding: 'utf-8' })
105
+ // }
106
+ // if (config?.console?.enabled === true) {
107
+ // if (fileTransport.levels[config?.console?.level || config?.level || 'error'] >= fileTransport.levels['info']) {
108
+ // console.log(msg)
109
+ // }
110
+ // }
111
+ // }
112
+ //
113
+ // fileTransport.on('finish', function(info) {
114
+ // // do something fun
115
+ // _file_log('Log done')
116
+ // })
117
+ // fileTransport.on('error', function(error) {
118
+ // // do something fun
119
+ // _file_log(red(`Error: ${error}`))
120
+ // })
121
+ //
125
122
  }
126
123
 
127
124
  // if (config?.file?.enabled === true) {
128
125
  // const datePattern = config?.file?.datePattern || 'YYYY-MM-DD'
129
126
  //
130
- // const fileTransport = new transports.DailyRotateFile({
127
+ // const fileTransport = new transports.DailyRotateFile({
131
128
  // level : config?.file?.level || config?.level || 'info' ,
132
129
  //
133
130
  // frequency: config?.file?.frequency,
@@ -135,21 +132,21 @@ const init_logger = (config, emailer, prefix= 'miolo') => {
135
132
  // zippedArchive: config?.file?.zippedArchive == true,
136
133
  // maxSize: config?.file?.maxSize || '20m',
137
134
  // maxFiles: config?.file?.maxFiles || '10d',
138
- //
139
- // filename : config?.file?.filename
135
+ //
136
+ // filename : config?.file?.filename
140
137
  // ? config.file.filename.replace('%MIOLO%', prefix)
141
- // : '/var/log/afialapis/miolo.%DATE%.log',
142
- //
138
+ // : '/var/log/afialapis/miolo.%DATE%.log',
139
+ //
143
140
  // auditFile: config?.file?.auditFile
144
141
  // ? config.file.auditFile.replace('%MIOLO%', prefix)
145
142
  // : '/var/log/afialapis/miolo.audit.json',
146
- //
143
+ //
147
144
  // symlinkName: config?.file?.symlinkName
148
145
  // ? config.file.symlinkName.replace('%MIOLO%', prefix)
149
146
  // : 'miolo.log',
150
147
  //
151
148
  // createSymlink: config?.file?.createSymlink == true,
152
- //
149
+ //
153
150
  // watchLog: true,
154
151
  //
155
152
  // humanReadableUnhandledException: true,
@@ -159,7 +156,7 @@ const init_logger = (config, emailer, prefix= 'miolo') => {
159
156
  // const _file_log = (s) => {
160
157
  // const currentDate = intre_to_str(intre_now(), datePattern)
161
158
  // const filename = path.join(fileTransport.dirname, fileTransport.filename.replace('%DATE%', currentDate))
162
- //
159
+ //
163
160
  // const msg = myFormat({
164
161
  // level: 'info',
165
162
  // message: `[logger][file-rotate] ${s}\n`,
@@ -205,19 +202,18 @@ const init_logger = (config, emailer, prefix= 'miolo') => {
205
202
  // _log_transports.push(fileTransport)
206
203
  // }
207
204
 
208
-
209
205
  // if (config?.file?.enabled === true) {
210
- // const fileTransport = new transports.File({
206
+ // const fileTransport = new transports.File({
211
207
  // level : config?.file?.level || config?.level || 'info' ,
212
208
  //
213
209
  // zippedArchive: config?.file?.zippedArchive == true,
214
210
  // maxsize: config?.file?.maxsize || (1024 * 1024 * 20),
215
211
  // maxFiles: config?.file?.maxFiles || 20,
216
- // //
217
- // filename : config?.file?.filename
212
+ // //
213
+ // filename : config?.file?.filename
218
214
  // ? config.file.filename.replace('%MIOLO%', prefix)
219
- // : '/var/log/afialapis/miolo.log',
220
- // //
215
+ // : '/var/log/afialapis/miolo.log',
216
+ // //
221
217
  //
222
218
  // humanReadableUnhandledException: true,
223
219
  // handleExceptions: true,
@@ -225,22 +221,21 @@ const init_logger = (config, emailer, prefix= 'miolo') => {
225
221
  // tailable: true,
226
222
  //
227
223
  // })
228
- //
224
+ //
229
225
  // _log_transports.push(fileTransport)
230
226
  // }
231
227
 
232
-
233
228
  //
234
229
  // Mail transport
235
230
  //
236
- if (config?.mail?.enabled === true) {
237
- const MailerLogger= init_logger_to_mail(config.mail, emailer)
231
+ if (emailer && config?.mail?.enabled === true) {
232
+ const MailerLogger = init_logger_to_mail(config.mail, emailer)
238
233
  transports.MailerLogger = MailerLogger
239
234
 
240
235
  _log_transports.push(
241
236
  new transports.MailerLogger({
242
237
  humanReadableUnhandledException: true,
243
- handleExceptions: true
238
+ handleExceptions: true
244
239
  })
245
240
  )
246
241
  }
@@ -249,12 +244,8 @@ const init_logger = (config, emailer, prefix= 'miolo') => {
249
244
  // Logger
250
245
  //
251
246
  const logger = createLogger({
252
- level: config?.level || 'silly',
253
- format: combine(
254
- errors({ stack: true }),
255
- timestamp(),
256
- printf(myFormat)
257
- ),
247
+ level: config?.level || "silly",
248
+ format: combine(errors({ stack: true }), injectStackTrace(), timestamp(), printf(myFormat)),
258
249
  transports: _log_transports
259
250
  })
260
251
 
@@ -262,52 +253,53 @@ const init_logger = (config, emailer, prefix= 'miolo') => {
262
253
  const _indented_func = (text, opts) => {
263
254
  try {
264
255
  if (opts?.section) {
265
- if (! (opts.section in _SECTIONS)) {
256
+ if (!(opts.section in _SECTIONS)) {
266
257
  // Section starts
267
- _SECTIONS[opts.section]= {
268
- indentIncr: parseInt(opts?.indent || _INDENT_SIZE),
258
+ _SECTIONS[opts.section] = {
259
+ indentIncr: parseInt(opts?.indent || _INDENT_SIZE, 10),
269
260
  timeStart: Date.now()
270
261
  }
271
262
  const nIndent = Math.max(_INDENT + _SECTIONS[opts.section].indentIncr, 0)
272
263
  f(text, opts)
273
- _INDENT= nIndent
274
-
264
+ _INDENT = nIndent
275
265
  } else {
276
266
  // Section ends
277
- const elapsed = parseFloat( (Date.now() - _SECTIONS[opts.section].timeStart) / 1000.0 ).toFixed(2)
267
+ const elapsed = parseFloat(
268
+ (Date.now() - _SECTIONS[opts.section].timeStart) / 1000.0
269
+ ).toFixed(2)
278
270
  text = `${text} (time: ${elapsed})`
279
- _INDENT-= _SECTIONS[opts.section].indentIncr
271
+ _INDENT -= _SECTIONS[opts.section].indentIncr
280
272
  f(text, opts)
281
- delete _SECTIONS[opts.section]
273
+ delete _SECTIONS[opts.section]
282
274
  }
283
275
  } else {
284
276
  f(text, opts)
285
277
  }
286
- } catch(error) {
278
+ } catch (error) {
287
279
  // TODO - How to exxpose info from here
288
280
  console.error(error)
289
-
290
281
  }
291
282
  }
292
- Object.defineProperty(_indented_func, "name", { value: name });
283
+ Object.defineProperty(_indented_func, "name", { value: name })
293
284
  return _indented_func
294
285
  }
295
286
 
296
- logger.error = _make_indented_log(logger.error, 'error')
297
- logger.warn = _make_indented_log(logger.warn, 'warn')
298
- logger.info = _make_indented_log(logger.info, 'info')
299
- logger.http = _make_indented_log(logger.http, 'http')
300
- logger.verbose = _make_indented_log(logger.verbose, 'verbose')
301
- logger.debug = _make_indented_log(logger.debug, 'debug')
302
- logger.silly = _make_indented_log(logger.silly, 'silly')
287
+ logger.error = _make_indented_log(logger.error, "error")
288
+ logger.warn = _make_indented_log(logger.warn, "warn")
289
+ logger.info = _make_indented_log(logger.info, "info")
290
+ logger.http = _make_indented_log(logger.http, "http")
291
+ logger.verbose = _make_indented_log(logger.verbose, "verbose")
292
+ logger.debug = _make_indented_log(logger.debug, "debug")
293
+ logger.silly = _make_indented_log(logger.silly, "silly")
303
294
 
304
295
  //console.log(logger.transports[0])
305
296
  try {
306
- logger.debug(`[logger] Inited for ${logger.transports.map(t => `${t.name} (${t.level})${t.silent ? red(' SILENT!') : ''}`).join(',')}`)
307
- } catch(_) {}
297
+ logger.debug(
298
+ `[logger] Inited for ${logger.transports.map((t) => `${t.name} (${t.level})${t.silent ? red(" SILENT!") : ""}`).join(",")}`
299
+ )
300
+ } catch (_) {}
308
301
 
309
302
  return logger
310
303
  }
311
304
 
312
-
313
- export {init_logger}
305
+ export { init_logger }