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,11 +1,6 @@
1
- import merge from 'deepmerge'
1
+ import merge from "deepmerge"
2
2
 
3
- import {
4
- DEFAULT_AUTH_USER,
5
- DEFAULT_USE_USER_FIELDS,
6
- DEFAULT_BEFORE_CALLBACK,
7
- DEFAULT_AFTER_CALLBACK
8
- } from "../defaults.mjs"
3
+ import { DEFAULT_AUTH_USER, DEFAULT_USE_USER_FIELDS } from "../defaults.mjs"
9
4
 
10
5
  /**
11
6
  [{
@@ -13,8 +8,8 @@ import {
13
8
 
14
9
  auth,
15
10
  bodyField,
16
- before: (ctx) => {return goon/!goon},
17
- after : (ctx, result) => {return result},
11
+ before: (ctx) => {return goon/!goon} or array of functions,
12
+ after : (ctx, data) => {return data} or array of functions,
18
13
 
19
14
  routes: an array of tables config, where each config can be:
20
15
  - a simple string with the table name
@@ -35,95 +30,76 @@ import {
35
30
 
36
31
  auth,
37
32
  bodyField,
38
- before: (ctx) => {return goon/!goon},
39
- after : (ctx, result) => {return result},
33
+ before: (ctx) => {return goon/!goon} or array of functions,
34
+ after : (ctx, data) => {return data} or array of functions,
40
35
 
41
36
  }
42
37
  }]
43
38
  */
44
39
 
45
40
  const getCrudConfig = (config) => {
41
+ const cruds = config?.crud || []
46
42
 
47
- const cruds= config?.crud || []
48
-
49
- if (! cruds) {
43
+ if (!cruds) {
50
44
  return []
51
45
  }
52
46
 
53
-
54
- if (! Array.isArray(cruds)) {
47
+ if (!Array.isArray(cruds)) {
55
48
  return []
56
49
  }
57
50
 
58
- let output= []
59
-
60
- cruds.map((crud) => {
61
- const routes= crud?.routes
51
+ const output = []
52
+
53
+ cruds.forEach((crud) => {
54
+ const routes = crud?.routes
62
55
 
63
- if (! routes) {
56
+ if (!routes) {
64
57
  return
65
58
  }
66
59
 
67
- if (! Array.isArray(routes)) {
60
+ if (!Array.isArray(routes)) {
68
61
  return
69
62
  }
70
-
63
+
71
64
  const comm_bodyField = crud?.bodyField || config?.bodyField
72
- const comm_before = crud?.before || config?.before || DEFAULT_BEFORE_CALLBACK
73
- const comm_after = crud?.after || config?.after || DEFAULT_AFTER_CALLBACK
65
+ const comm_before = crud?.before || config?.before
66
+ const comm_after = crud?.after || config?.after
74
67
 
68
+ const comm_auth = merge.all([DEFAULT_AUTH_USER, config?.auth || {}, crud?.auth || {}])
75
69
 
76
- const comm_auth= merge.all([
77
- DEFAULT_AUTH_USER,
78
- config?.auth || {},
79
- crud?.auth || {}
80
- ])
81
-
82
- let parsed_routes= []
70
+ const parsed_routes = []
83
71
 
84
72
  for (const troute of routes) {
73
+ const route = typeof troute === "string" ? { name: troute } : troute
85
74
 
86
- const route = (typeof troute === 'string')
87
- ? {name: troute}
88
- : troute
89
-
90
- if (! route.name) {
75
+ if (!route.name) {
91
76
  continue
92
77
  }
93
78
 
94
- const parsed_route= {
79
+ const parsed_route = {
95
80
  name: route.name,
96
81
  url: route?.url || route.name,
97
- mode: route?.mode || 'rw',
82
+ mode: route?.mode || "rw",
98
83
 
99
84
  bodyField: route?.bodyField || comm_bodyField,
100
- useUserFields: merge.all([
101
- DEFAULT_USE_USER_FIELDS,
102
- route?.useUserFields || {}
103
- ]),
104
- auth: merge.all([
105
- comm_auth,
106
- route?.auth || {}
107
- ]),
85
+ useUserFields: merge.all([DEFAULT_USE_USER_FIELDS, route?.useUserFields || {}]),
86
+ auth: merge.all([comm_auth, route?.auth || {}]),
108
87
 
109
88
  before: route?.before || comm_before,
110
89
  after: route?.after || comm_after
111
90
  }
112
91
  parsed_routes.push(parsed_route)
113
92
  }
114
-
115
- if (parsed_routes.length>0) {
116
- output.push( {
117
- prefix: crud?.prefix || '',
93
+
94
+ if (parsed_routes.length > 0) {
95
+ output.push({
96
+ prefix: crud?.prefix || "",
118
97
  routes: parsed_routes
119
- } )
98
+ })
120
99
  }
121
-
122
100
  })
123
101
 
124
102
  return output
125
-
126
103
  }
127
104
 
128
-
129
105
  export default getCrudConfig
@@ -1,29 +1,16 @@
1
- const DEFAULT_BEFORE_CALLBACK = async (ctx) => {
2
- return true
3
- }
4
-
5
- const DEFAULT_AFTER_CALLBACK = async (ctx, result) => {
6
- return result
7
- }
8
-
9
1
  const DEFAULT_AUTH_USER = {
10
- require: false, // true / false / 'read-only'
11
- action: 'redirect', // 'error'
12
- redirect_url: '/',
2
+ require: false, // true / false / 'read-only'
3
+ action: "redirect", // 'error'
4
+ redirect_url: "/",
13
5
  error_code: 401
14
6
  }
15
7
 
16
8
  const DEFAULT_USE_USER_FIELDS = {
17
9
  use: false,
18
10
  fieldNames: {
19
- created_by: 'created_by',
20
- last_update_by: 'last_update_by'
11
+ created_by: "created_by",
12
+ last_update_by: "last_update_by"
21
13
  }
22
14
  }
23
15
 
24
- export {
25
- DEFAULT_AUTH_USER,
26
- DEFAULT_USE_USER_FIELDS,
27
- DEFAULT_BEFORE_CALLBACK,
28
- DEFAULT_AFTER_CALLBACK
29
- }
16
+ export { DEFAULT_AUTH_USER, DEFAULT_USE_USER_FIELDS }
@@ -1,28 +1,27 @@
1
- import Router from '@koa/router'
2
- import getCrudConfig from './crud/getCrudConfig.mjs'
3
- import attachCrudRoutes from './crud/attachCrudRoutes.mjs'
4
- import getQueriesConfig from './queries/getQueriesConfig.mjs'
5
- import attachQueriesRoutes from './queries/attachQueriesRoutes.mjs'
6
-
1
+ import Router from "@koa/router"
2
+ import attachCrudRoutes from "./crud/attachCrudRoutes.mjs"
3
+ import getCrudConfig from "./crud/getCrudConfig.mjs"
4
+ import attachQueriesRoutes from "./queries/attachQueriesRoutes.mjs"
5
+ import getQueriesConfig from "./queries/getQueriesConfig.mjs"
7
6
 
8
7
  function init_router(app, routes) {
9
8
  const logger = app.context.miolo.logger
10
9
 
11
10
  // Init the Koa Router
12
11
  const router = new Router()
13
-
12
+
14
13
  try {
15
14
  // Parse routes
16
- const crudConfigs= getCrudConfig(routes)
17
- const queriesConfigs= getQueriesConfig(routes)
18
-
15
+ const crudConfigs = getCrudConfig(routes)
16
+ const queriesConfigs = getQueriesConfig(routes)
17
+
19
18
  // check routes
20
- const crudConfigsOk= crudConfigs.length > 0
21
- const queriesConfigsOk= queriesConfigs.length > 0
19
+ const crudConfigsOk = crudConfigs.length > 0
20
+ const queriesConfigsOk = queriesConfigs.length > 0
22
21
 
23
- if ( (!crudConfigsOk) && (!queriesConfigsOk)) {
22
+ if (!crudConfigsOk && !queriesConfigsOk) {
24
23
  throw "[router] Could not get any route from the passed <routes> param"
25
- }
24
+ }
26
25
 
27
26
  // attach CRUD routes
28
27
  if (crudConfigsOk) {
@@ -33,17 +32,14 @@ function init_router(app, routes) {
33
32
  if (queriesConfigsOk) {
34
33
  attachQueriesRoutes(router, queriesConfigs, logger)
35
34
  }
36
- } catch(e) {
37
- logger.error('[router] Error initing the router.')
38
- logger.error('[router] routes:')
35
+ } catch (e) {
36
+ logger.error("[router] Error initing the router.")
37
+ logger.error("[router] routes:")
39
38
  logger.error(routes)
40
39
  logger.error(e)
41
-
42
40
  }
43
41
 
44
42
  app.use(router.routes())
45
43
  }
46
44
 
47
-
48
-
49
- export {init_router}
45
+ export { init_router }
@@ -1,102 +1,279 @@
1
- import {query_string_to_json} from '../utils.mjs'
1
+ import Joi from "joi"
2
+ import { ensure_response_is_ok_data, query_string_to_json } from "../utils.mjs"
2
3
 
3
4
  function attachQueriesRoutes(router, queriesConfigs, logger) {
5
+ queriesConfigs.forEach((queriesConfig) => {
6
+ const prefix = queriesConfig.prefix
4
7
 
5
- queriesConfigs.map((queriesConfig) => {
6
-
7
- const prefix= queriesConfig.prefix
8
-
9
- queriesConfig.routes.map(route => {
10
-
11
- let url = (!prefix)
12
- ? `/${route.url}`
13
- : `/${prefix}/${route.url}`
14
- while (url.indexOf('//')>=0) {
15
- url= url.replace(/\/\//g, "/")
8
+ queriesConfig.routes.forEach((route) => {
9
+ let url = !prefix ? `/${route.url}` : `/${prefix}/${route.url}`
10
+ while (url.indexOf("//") >= 0) {
11
+ url = url.replace(/\/\//g, "/")
16
12
  }
17
13
 
18
14
  const routeAuth = route.auth
19
- const checkAuth= (routeAuth.require===true) || (routeAuth.require==='read-only' && route.method==='POST')
20
-
21
- logger.debug(`[router] Routing ${route.callback?.name || 'callback'} to ${route.method} ${url}${checkAuth ? ' (auth)' : ''}`)
15
+ const checkAuth =
16
+ routeAuth.require === true || (routeAuth.require === "read-only" && route.method === "POST")
17
+
18
+ logger.debug(
19
+ `[router] Routing ${route.callback?.name || "callback"} to ${route.method} ${url}${checkAuth ? " (auth)" : ""}`
20
+ )
22
21
 
23
22
  const _route_auth_callback = async (ctx) => {
24
-
25
23
  if (checkAuth) {
26
- const authenticated= ctx?.session?.authenticated === true
24
+ const authenticated = ctx?.session?.authenticated === true
27
25
  if (!authenticated) {
28
- if (routeAuth.action=='error') {
26
+ if (routeAuth.action === "error") {
29
27
  ctx.miolo.logger.error(`Unauthorized access. Throwing error ${routeAuth.error_code}`)
30
- ctx.throw(
31
- routeAuth.error_code,
32
- 'Unauthorized',
33
- {}
34
- )
35
- } else if (routeAuth.action=='redirect') {
28
+ ctx.throw(routeAuth.error_code, new Error("Unauthorized"), {})
29
+ } else if (routeAuth.action === "redirect") {
36
30
  ctx.miolo.logger.warn(`Unauthorized access. Redirecting to ${routeAuth.redirect_url}`)
37
31
  ctx.redirect(routeAuth.redirect_url)
38
32
  } else {
39
33
  ctx.miolo.logger.error(`Route path ${route.url} specified auth but no action`)
40
- ctx.body= {}
41
34
  }
42
35
  }
43
36
 
44
37
  return authenticated
45
38
  }
46
-
39
+
47
40
  return true
48
41
  }
49
42
 
50
43
  const _route_callback = async (ctx) => {
51
- let result = {}
52
44
  try {
53
45
  try {
54
- if ((route.method == 'GET') && (!ctx.request?.body)) {
55
- if (ctx.request.url.indexOf('?')>0) {
56
- const fields= query_string_to_json(ctx.request.url)
46
+ if (route.method === "GET" && !ctx.request?.body) {
47
+ if (ctx.request.url.indexOf("?") > 0) {
48
+ const fields = query_string_to_json(ctx.request.url)
57
49
  if (fields) {
58
- ctx.request.body= fields
50
+ ctx.request.body = fields
59
51
  }
60
52
  }
61
53
  }
62
- } catch(e) {
63
- ctx.miolo.logger.error(`[router] Error while trying to qet query params for ${ctx.request.url}`)
54
+ } catch (error) {
55
+ ctx.miolo.logger.error(
56
+ `[router] Error while trying to qet query params for ${ctx.request.url}: ${error?.message || "?"}`
57
+ )
64
58
  }
65
59
 
66
60
  const authenticated = await _route_auth_callback(ctx)
67
- if (! authenticated) {
61
+ if (!authenticated) {
62
+ ctx.body = {
63
+ ok: false,
64
+ error: "Not authorized"
65
+ }
68
66
  return
69
67
  }
70
-
71
- let goon= true
68
+
69
+ let goon = true
72
70
  if (route?.before) {
73
- goon= await route.before(ctx)
71
+ if (Array.isArray(route.before)) {
72
+ for (const before of route.before) {
73
+ goon = await before(ctx)
74
+ if (!goon) {
75
+ break
76
+ }
77
+ }
78
+ } else {
79
+ goon = await route.before(ctx)
80
+ }
81
+
82
+ if (!goon) {
83
+ ctx.body = {
84
+ ok: false,
85
+ error: `Route was aborted by a <before> callback (${route.before?.name})`
86
+ }
87
+ return
88
+ }
74
89
  }
75
90
 
76
- if (! goon) {
77
- return
91
+ const inputSchema = route?.schema?.input
92
+
93
+ if (inputSchema) {
94
+ // Check schema is actually a schema
95
+ if (!Joi.isSchema(inputSchema)) {
96
+ ctx.miolo.logger.error(
97
+ `[router] Expecting input schema at ${url} but something else was found (${typeof inputSchema})`
98
+ )
99
+ ctx.body = {
100
+ ok: false,
101
+ error: "Invalid input schema"
102
+ }
103
+ return
104
+ }
105
+
106
+ try {
107
+ const v = inputSchema.validate(ctx.request.body)
108
+
109
+ if (v?.error) {
110
+ ctx.miolo.logger.warn(
111
+ `[router] Input schema invalidated data for ${url}: ${v.error}\n${v.error.annotate(true)}`
112
+ )
113
+ ctx.body = {
114
+ ok: false,
115
+ error: v.error.toString()
116
+ }
117
+ return
118
+ } else if (v?.value) {
119
+ ctx.miolo.logger.silly(
120
+ `[router] Input schema validated data for ${url} successfully`
121
+ )
122
+ ctx.request.body = v.value
123
+ } else {
124
+ const description = inputSchema.describe()
125
+
126
+ // Check if schema was deliberately set to allow only null
127
+ // schema = Joi.any().allow(null)
128
+ const isOnlyNull =
129
+ description.type === "any" &&
130
+ description.allow &&
131
+ description.allow.length === 1 &&
132
+ description.allow[0] === null
133
+
134
+ if (isOnlyNull) {
135
+ ctx.miolo.logger.silly(
136
+ `[router] Input schema allowed null param to ${url} successfully`
137
+ )
138
+ ctx.request.body = v.value
139
+ } else {
140
+ ctx.miolo.logger.warn(
141
+ `[router] Input schema returned unknown result for ${url}: ${JSON.stringify(v)}. Let's ignore it.`
142
+ )
143
+ }
144
+ }
145
+ } catch (error) {
146
+ ctx.miolo.logger.error(
147
+ `[router] Error validating input schema at ${url}: ${error?.message || error}`
148
+ )
149
+ ctx.body = {
150
+ ok: false,
151
+ error: error?.message || error
152
+ }
153
+ return
154
+ }
155
+ }
156
+
157
+ const result = await route.callback(ctx, ctx.request.body)
158
+
159
+ if (!route.keep_body) {
160
+ if (ctx.body !== undefined) {
161
+ ctx.body = ensure_response_is_ok_data(ctx, ctx.body)
162
+ } else {
163
+ ctx.body = ensure_response_is_ok_data(ctx, result)
164
+ }
78
165
  }
79
-
80
- result= await route.callback(ctx)
81
-
166
+
82
167
  if (route?.after) {
83
- result= await route.after(ctx, result)
168
+ if (Array.isArray(route.after)) {
169
+ for (const after of route.after) {
170
+ const result = await after(ctx, ctx.body)
171
+ ctx.body = ensure_response_is_ok_data(ctx, result)
172
+ }
173
+ } else {
174
+ const result = await route.after(ctx, ctx.body)
175
+ ctx.body = ensure_response_is_ok_data(ctx, result)
176
+ }
177
+ }
178
+
179
+ // If body has no ok and read data, do not validate it
180
+ if (ctx.body?.ok !== true || !ctx.body?.data) {
181
+ return
182
+ }
183
+
184
+ const outputSchema = route?.schema?.output
185
+
186
+ if (outputSchema) {
187
+ // Check schema is actually a schema
188
+ if (!Joi.isSchema(outputSchema)) {
189
+ ctx.miolo.logger.error(
190
+ `[router] Expecting output schema at ${url} but something else was found (${typeof outputSchema})`
191
+ )
192
+ ctx.body = {
193
+ ok: false,
194
+ error: "Invalid output schema"
195
+ }
196
+ return
197
+ }
198
+
199
+ // perform validation over the result
200
+ let v
201
+ try {
202
+ v = outputSchema.validate(ctx.body.data, { stripUnknown: true })
203
+ } catch (uerror) {
204
+ const error = `Unexpected error validating output data for ${url}: ${uerror?.message || uerror}`
205
+ ctx.miolo.logger.warn(`[router] ${error}`)
206
+ ctx.body = {
207
+ ok: false,
208
+ error: error
209
+ }
210
+ return
211
+ }
212
+
213
+ // raise validation errors
214
+ if (v?.error) {
215
+ ctx.miolo.logger.warn(
216
+ `[router] Output schema invalidated data for ${url}: ${v.error}\n${v.error.annotate(true)}`
217
+ )
218
+ ctx.body = {
219
+ ok: false,
220
+ error: v.error.toString()
221
+ }
222
+ return
223
+ }
224
+
225
+ if (v?.value) {
226
+ const diff = diffObjs(ctx.body.data, v.value)
227
+ if (diff) {
228
+ ctx.miolo.logger.debug(`[router] Output schema has removed ${diff} for URL ${url}`)
229
+ }
230
+
231
+ ctx.miolo.logger.silly(
232
+ `[router] Output schema validated data for ${url} successfully`
233
+ )
234
+ ctx.body.data = v.value
235
+ }
236
+
237
+ // check parsed value is ok
238
+ if (!v?.value) {
239
+ const description = outputSchema.describe()
240
+
241
+ // Check if schema was deliberately set to allow only null
242
+ // schema = Joi.any().allow(null)
243
+ const isOnlyNull =
244
+ description.type === "any" &&
245
+ description.allow &&
246
+ description.allow.length === 1 &&
247
+ description.allow[0] === null
248
+
249
+ if (!isOnlyNull) {
250
+ const error = `Output schema returned unknown result for ${fn.name}: ${JSON.stringify(v)}`
251
+ ctx.miolo.logger.silly(`[router][${fn.name}] ${error}`)
252
+ ctx.body = {
253
+ ok: false,
254
+ error: error
255
+ }
256
+ return
257
+ }
258
+ }
84
259
  }
85
- } catch(error) {
86
- ctx.miolo.logger.error(`[router] Unexpected error on Query ${route.callback?.name} at ${url}`)
260
+ } catch (error) {
261
+ ctx.miolo.logger.error(
262
+ `[router] Unexpected error on Query ${route.callback?.name} at ${url}`
263
+ )
87
264
  ctx.miolo.logger.error(error)
265
+ ctx.body = {
266
+ ok: false,
267
+ error: error?.message || error
268
+ }
88
269
  }
89
-
90
- return result
91
270
  }
92
271
 
93
272
  const router_method = route.method.toLowerCase()
94
273
 
95
274
  router[router_method](url, (ctx) => _route_callback(ctx, route))
96
-
97
275
  })
98
-
99
276
  })
100
277
  }
101
278
 
102
- export default attachQueriesRoutes
279
+ export default attachQueriesRoutes