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,19 +1,13 @@
1
- import merge from 'deepmerge'
2
-
3
- import {
4
- DEFAULT_AUTH_USER,
5
- DEFAULT_BEFORE_CALLBACK,
6
- DEFAULT_AFTER_CALLBACK
7
- } from "../defaults.mjs"
8
- import { make_endpoint_from_fn } from '../utils.mjs'
1
+ import merge from "deepmerge"
2
+ import { DEFAULT_AUTH_USER } from "../defaults.mjs"
9
3
 
10
4
  /**
11
5
  {
12
6
  prefix: '/queries',
13
7
 
14
8
  auth,
15
- before: (ctx) => {return goon/!goon},
16
- after : (ctx, result) => {return result},
9
+ before: (ctx) => {return goon/!goon} or array of functions,
10
+ after : (ctx, data) => {return data} or array of functions,
17
11
 
18
12
 
19
13
  routes: [
@@ -21,88 +15,84 @@ import { make_endpoint_from_fn } from '../utils.mjs'
21
15
  {
22
16
  url: '/crud/todos/fake',
23
17
  method: 'GET', // 'POST'
24
- callback: async (ctx) => { ctx.body = result } ,
25
- // or
26
- callback_fn: async (miolo, params) => { return result } ,
27
-
18
+ callback: async (ctx, params) => {
19
+ return {ok: true/false, data|error}
20
+ // or:
21
+ // return <anything>
22
+ // and milo will wrap into {ok: true, data: <anything>}
23
+ // or by yourself:
24
+ // ctx.body = {ok: true/false, data|error}
25
+ // (you may want {keep_body: true})
26
+ } ,
28
27
  auth,
29
- before: (ctx) => {return goon/!goon},
30
- after : (ctx, result) => {return result},
28
+ before: async (ctx) => { return true/false } or array of functions,
29
+ after : async (ctx, data) => { return data } or array of functions,
30
+ schema: {
31
+ input: a Joi schema for validating input data,
32
+ output: a Joi schema for validating output data,
33
+ },
34
+ keep_body: false by default. If true, miolo wont wnsure ctx.body after callback.
31
35
  }
32
36
  ]
33
37
  }
34
38
  */
35
39
 
36
40
  const getQueriesConfig = (config) => {
41
+ const instances = config?.queries || []
37
42
 
38
-
39
- const instances= config?.queries || []
40
-
41
- if (! instances) {
43
+ if (!instances) {
42
44
  return []
43
45
  }
44
46
 
45
-
46
- if (! Array.isArray(instances)) {
47
+ if (!Array.isArray(instances)) {
47
48
  return []
48
49
  }
49
50
 
50
- let output= []
51
-
52
- instances.map((instance) => {
51
+ const output = []
53
52
 
54
- const routes= instance?.routes
53
+ instances.forEach((instance) => {
54
+ const routes = instance?.routes
55
55
 
56
- if (! routes) {
56
+ if (!routes) {
57
57
  return
58
58
  }
59
59
 
60
- if (! Array.isArray(routes)) {
60
+ if (!Array.isArray(routes)) {
61
61
  return
62
62
  }
63
-
64
- const comm_before = instance?.before || config?.before || DEFAULT_BEFORE_CALLBACK
65
- const comm_after = instance?.after || config?.after || DEFAULT_AFTER_CALLBACK
66
-
67
- const comm_auth= merge.all([
68
- DEFAULT_AUTH_USER,
69
- instance?.auth || {},
70
- config?.auth || {}
71
- ])
72
-
73
- let parsed_routes= []
63
+
64
+ const comm_before = instance?.before || config?.before
65
+ const comm_after = instance?.after || config?.after
66
+
67
+ const comm_auth = merge.all([DEFAULT_AUTH_USER, instance?.auth || {}, config?.auth || {}])
68
+
69
+ const parsed_routes = []
74
70
 
75
71
  for (const route of routes) {
76
- if (! route.url) {
72
+ if (!route.url) {
77
73
  continue
78
74
  }
79
-
80
- let cb=undefined
81
- if ((! route.callback) && (! route.callback_fn)) {
82
- continue
83
- } else {
84
- cb = route.callback || make_endpoint_from_fn(route.callback_fn)
85
- }
86
75
 
87
- const parsed_route= {
76
+ const cb = route?.callback
77
+
78
+ const parsed_route = {
88
79
  url: route.url,
89
- method: route?.method || 'GET',
80
+ method: route?.method || "GET",
90
81
  callback: cb,
91
82
 
92
- auth: merge.all([
93
- comm_auth,
94
- route?.auth || {}
95
- ]),
83
+ auth: merge.all([comm_auth, route?.auth || {}]),
96
84
 
97
85
  before: route?.before || comm_before,
98
- after: route?.after || comm_after
86
+ after: route?.after || comm_after,
87
+ schema: route?.schema,
88
+ keep_body: route?.keep_body === true
99
89
  }
100
90
 
101
91
  parsed_routes.push(parsed_route)
102
92
  }
103
93
 
104
94
  output.push({
105
- prefix: instance?.prefix || '',
95
+ prefix: instance?.prefix || "",
106
96
  routes: parsed_routes
107
97
  })
108
98
  })
@@ -1,38 +1,53 @@
1
1
  /**
2
2
  * Transform a query string to a JSON object
3
3
  */
4
- import qs from 'qs'
4
+ import qs from "qs"
5
5
 
6
- export function query_string_to_json(url) {
7
- let search= url.indexOf('?')>=0 ? url.substr(url.indexOf('?')+1) : '';
8
- if (search) {
9
- return qs.parse(search)
10
- }
11
- return {}
12
- }
6
+ export function query_string_to_json(url) {
7
+ const search = url.includes("?") ? url.substring(url.indexOf("?") + 1) : ""
8
+ if (!search) return {}
13
9
 
14
- export function make_endpoint_from_fn(fn, field) {
15
-
16
- async function endpoint_from_fn(ctx) {
17
- const params = ctx.request.body
18
- try {
19
- ctx.miolo.logger.debug(`[router] ${fn.name}() Calling with params ${JSON.stringify(params)}`)
20
- } catch (_) {
21
- ctx.miolo.logger.debug(`[router] ${fn.name}()Calling with params (?)`)
10
+ const parsed = qs.parse(search)
11
+ //
12
+ function reviveTypes(value) {
13
+ if (Array.isArray(value)) return value.map(reviveTypes)
14
+ if (value && typeof value === "object") {
15
+ const result = {}
16
+ for (const key in value) {
17
+ if (Object.hasOwn(value, key)) result[key] = reviveTypes(value[key])
18
+ }
19
+ return result
22
20
  }
21
+ // Try to convert to boolean, number o null
22
+ if (value === "true") return true
23
+ if (value === "false") return false
24
+ if (value === "null") return null
25
+ if (!Number.isNaN(Number(value)) && value !== "") return Number(value)
26
+ return value
27
+ }
23
28
 
24
- const result = await fn(ctx.miolo, params)
29
+ return reviveTypes(parsed)
30
+ }
25
31
 
26
- try {
27
- ctx.miolo.logger.debug(`[router] ${fn.name}() Called with result ${JSON.stringify(result)}`)
28
- } catch (_) {
29
- ctx.miolo.logger.debug(`[router] ${fn.name}() Called with result (?)`)
32
+ export function ensure_response_is_ok_data(ctx, response) {
33
+ if (response?.ok === undefined && response?.data === undefined && response?.error === undefined) {
34
+ ctx.miolo.logger.debug(
35
+ `[router] Response without ok/[data/error] fields. It is: ${JSON.stringify(response)}. Let's wrap it on an ok response`
36
+ )
37
+ return {
38
+ ok: true,
39
+ data: response || {}
40
+ }
41
+ }
42
+ if (response?.error !== undefined) {
43
+ return {
44
+ ok: false,
45
+ error: response.error,
46
+ data: response?.data
30
47
  }
31
-
32
- ctx.body = result
33
48
  }
34
-
35
- Object.defineProperty(endpoint_from_fn, 'name', {value: fn.name, writable: false})
36
49
 
37
- return endpoint_from_fn
50
+ const ok = response?.ok !== false
51
+ const data = response?.data !== undefined ? response.data : response !== undefined ? response : {}
52
+ return { ok, data }
38
53
  }
@@ -1,12 +1,10 @@
1
-
2
1
  // context builder
3
- export const ssr_context_builder_make = (app, ssrConfig) => {
4
-
2
+ export const ssr_context_builder_make = (_app, _ssrConfig) => {
5
3
  const ssr_build_context = (ctx, config, ssr_data) => {
6
4
  const isAuthed = ctx?.session?.authenticated === true
7
5
  const user = ctx?.session?.user
8
-
9
- const context= {
6
+
7
+ const context = {
10
8
  config,
11
9
  user,
12
10
  authenticated: isAuthed,
@@ -16,6 +14,6 @@ export const ssr_context_builder_make = (app, ssrConfig) => {
16
14
 
17
15
  return context
18
16
  }
19
-
17
+
20
18
  return ssr_build_context
21
- }
19
+ }
@@ -1,18 +1,30 @@
1
- import path from 'path'
2
- import { readFileSync } from 'fs'
3
- import { fallbackIndexHTML } from './fallbackIndex.mjs'
1
+ import { readFileSync } from "node:fs"
2
+ import path from "node:path"
3
+ import { nanoid } from "nanoid"
4
+ import { fallbackIndexHTML } from "./fallbackIndex.mjs"
5
+
6
+ let _script_version
7
+ const get_script_version = () => {
8
+ if (!_script_version) {
9
+ _script_version = nanoid()
10
+ }
11
+ return _script_version
12
+ }
4
13
 
5
14
  function _html_read(htmlFile) {
15
+ if (!htmlFile) {
16
+ return fallbackIndexHTML
17
+ }
6
18
  try {
7
- const isProduction = process.env.NODE_ENV === 'production'
19
+ const isProduction = process.env.NODE_ENV === "production"
8
20
  const proot = (p) => path.join(process.cwd(), p)
9
- const indexHTMLPath= proot(isProduction
10
- ? `${process.env.MIOLO_BUILD_CLIENT_DEST}/index.html`
11
- : htmlFile)
21
+ const indexHTMLPath = proot(
22
+ isProduction ? `${process.env.MIOLO_BUILD_CLIENT_DEST}/index.html` : htmlFile
23
+ )
12
24
 
13
- const indexHTML = readFileSync(indexHTMLPath, 'utf8')
25
+ const indexHTML = readFileSync(indexHTMLPath, "utf8")
14
26
  return indexHTML
15
- } catch(error) {
27
+ } catch (error) {
16
28
  console.error(`[miolo] Error reading HTML file ${htmlFile}: ${error}`)
17
29
  return fallbackIndexHTML
18
30
  }
@@ -21,80 +33,91 @@ function _html_read(htmlFile) {
21
33
  function _feed_html(htmlString, client, context, ssr_html) {
22
34
  const contextScript = `<script> window.__CONTEXT = ${JSON.stringify(context, null, 2)}</script>`
23
35
  const rootDiv = `<div id="root">${ssr_html}</div>`
24
-
25
- const webClient = client.startsWith('./')
26
- ? client.replace('./', '/')
27
- : client.startsWith('/')
28
- ? client
29
- : `/${client}`
36
+
37
+ const webClient = client.startsWith("./")
38
+ ? client.replace("./", "/")
39
+ : client.startsWith("/")
40
+ ? client
41
+ : `/${client}`
30
42
 
31
43
  const linkTag =
32
- process.env.NODE_ENV === 'production'
33
- ? ` <script src="${webClient}" async></script>`
44
+ process.env.NODE_ENV === "production"
45
+ ? ` <script src="${webClient}?v=${get_script_version()}" async></script>`
34
46
  : ` <script type="module" src="${webClient}"></script>`
35
-
47
+
36
48
  // head
37
49
  const headCloseTagRegex = /(<\/head>)/i
38
- htmlString= htmlString.replace(headCloseTagRegex, `${contextScript}\n$&`)
39
-
50
+ htmlString = htmlString.replace(headCloseTagRegex, `${contextScript}\n$&`)
51
+
40
52
  // body
41
53
  const bodyCloseTagRegex = /(<\/body>)/i
42
- htmlString= htmlString.replace(bodyCloseTagRegex, `${rootDiv}\n${linkTag}\n$&`)
43
-
54
+ htmlString = htmlString.replace(bodyCloseTagRegex, `${rootDiv}\n${linkTag}\n$&`)
55
+
44
56
  return htmlString
45
57
  }
46
58
 
47
-
48
59
  // HTML renderer
49
- export const ssr_html_renderer_make = async (app, ssrConfig, htmlFile, client, devRender= undefined) => {
50
- const isProduction = process.env.NODE_ENV === 'production'
60
+ export const ssr_html_renderer_make = async (
61
+ app,
62
+ ssrConfig,
63
+ htmlFile,
64
+ client,
65
+ devRender = undefined
66
+ ) => {
67
+ const isProduction = process.env.NODE_ENV === "production"
51
68
 
52
69
  // check HTML
53
70
  const tmplHtml = _html_read(htmlFile)
54
71
 
55
72
  const ssr_html_renderer = async (ctx, context) => {
56
- let base_html= tmplHtml
57
-
73
+ let base_html = tmplHtml
74
+
58
75
  // prepare render() function for server entry
59
76
 
60
77
  // fallback function
61
- let render = (ctx, context) => ''
78
+ let render = (_ctx, _context) => ""
62
79
  // if vite and DEV
63
- if ((!isProduction) && (app?.vite)) {
80
+ if (!isProduction && app?.vite) {
64
81
  if (devRender) {
65
82
  const [dev_html, dev_render] = await devRender(app, ctx, base_html, ssrConfig)
66
83
  if (dev_render) {
67
- base_html= dev_html
68
- render= dev_render
84
+ base_html = dev_html
85
+ render = dev_render
69
86
  }
70
87
  }
71
- // if non-vite or prod
88
+ // if non-vite or prod
72
89
  } else {
73
90
  try {
74
- render = (await import(ssrConfig.server)).render
75
- } catch(error) {
76
- ctx.miolo.logger.error(`SSR Error for ${ssrConfig.server}:\n${error.toString()}\n${error.stack}`)
91
+ if (ssrConfig?.server !== "false") {
92
+ render = (await import(ssrConfig.server)).render
93
+ }
94
+ } catch (error) {
95
+ ctx.miolo.logger.error(
96
+ `SSR Error for ${ssrConfig.server}:\n${error.toString()}\n${error.stack}`
97
+ )
77
98
  }
78
99
  }
79
100
 
80
101
  // render the result
81
- let ssr_html= ''
102
+ let ssr_html = ""
82
103
  try {
83
104
  ssr_html = render(ctx, context)
84
- } catch(error) {
85
- ctx.miolo.logger.error(`SSR Error rendering server entry:\n${error.toString()}\n${error.stack}`)
86
-
87
- ssr_html= `
105
+ } catch (error) {
106
+ ctx.miolo.logger.error(
107
+ `SSR Error rendering server entry:\n${error.toString()}\n${error.stack}`
108
+ )
109
+
110
+ ssr_html = `
88
111
  <div>
89
112
  MIOLO: SSR Error: ${error.stack}
90
113
  </div>
91
114
  `
92
115
  }
93
-
116
+
94
117
  const parsed_html = _feed_html(base_html, client, context, ssr_html)
95
118
 
96
119
  return parsed_html
97
120
  }
98
-
121
+
99
122
  return ssr_html_renderer
100
- }
123
+ }
@@ -1,24 +1,21 @@
1
1
  // ssr loader
2
- export const ssr_loader_make = (app, ssrConfig) => {
3
-
2
+ export const ssr_loader_make = (_app, ssrConfig) => {
4
3
  const ssr_loader = async (ctx) => {
5
- let res= {}
4
+ let res = {}
6
5
  try {
7
6
  if (ssrConfig?.loader) {
8
- res= await ssrConfig.loader(ctx)
7
+ res = await ssrConfig.loader(ctx)
9
8
  }
10
- } catch(e) {
11
- const tit= 'Error produced by loader in ssrConfig'
12
- const inf= `URL: ${ctx.request.url}\nFields: ${JSON.stringify(ctx.request?.fields || {})}`
13
- const det= e?.stack
14
- ? `${e.toString()}\n${e.stack}`
15
- : e.toString()
16
- const all= `${tit}\n${inf}\n${det}`
17
-
9
+ } catch (e) {
10
+ const tit = "Error produced by loader in ssrConfig"
11
+ const inf = `URL: ${ctx.request.url}\nFields: ${JSON.stringify(ctx.request?.fields || {})}`
12
+ const det = e?.stack ? `${e.toString()}\n${e.stack}` : e.toString()
13
+ const all = `${tit}\n${inf}\n${det}`
14
+
18
15
  ctx.miolo.logger.error(all)
19
16
  }
20
- return res
17
+ return res
21
18
  }
22
19
 
23
20
  return ssr_loader
24
- }
21
+ }
@@ -1,46 +1,63 @@
1
- import { ssr_context_builder_make } from './context.mjs'
2
- import { ssr_loader_make } from './loader.mjs'
3
- import { ssr_html_renderer_make } from './html.mjs'
4
-
5
-
6
- export async function init_ssr_render_middleware(app, config, devRender= undefined) {
1
+ import { ssr_context_builder_make } from "./context.mjs"
2
+ import { ssr_html_renderer_make } from "./html.mjs"
3
+ import { ssr_loader_make } from "./loader.mjs"
7
4
 
5
+ export async function init_ssr_render_middleware(app, config, devRender = undefined) {
8
6
  const ssrConfig = config.build.ssr
9
7
  const httpConfig = config.http
10
8
  const authConfig = config?.auth || {}
11
- // const socketConfig = config?.socket || {}
9
+ const socketConfig = config?.socket || {}
12
10
 
13
11
  const ssr_build_context = ssr_context_builder_make(app, ssrConfig)
14
12
  const ssr_loader = ssr_loader_make(app, ssrConfig)
15
- const ssr_html_renderer = await ssr_html_renderer_make(app, ssrConfig, config.build.html, config.build.client, devRender)
13
+ const ssr_html_renderer = await ssr_html_renderer_make(
14
+ app,
15
+ ssrConfig,
16
+ config.build.html,
17
+ config.build.client,
18
+ devRender
19
+ )
16
20
 
17
21
  async function render_ssr_middleware(ctx) {
18
22
  try {
19
- const config= {
23
+ const config = {
20
24
  hostname: httpConfig?.hostname,
21
25
  port: httpConfig?.port,
22
26
  catcher_url: httpConfig?.catcher_url,
23
- login_url: authConfig?.credentials?.url_login,
24
- logout_url: authConfig?.credentials?.url_logout,
25
- //socket: {
26
- // enabled: socketConfig?.enabled===true,
27
- // config: socketConfig?.config?.cli || {}
28
- //}
27
+ auth_method: ctx.session?.auth_method,
28
+ log_level: ctx.miolo.logger.level,
29
+ login_url:
30
+ ctx.session?.auth_method === "google"
31
+ ? authConfig?.passport?.google_url_login
32
+ : authConfig?.passport?.local_url_login,
33
+ logout_url:
34
+ ctx.session?.auth_method === "google"
35
+ ? authConfig?.passport?.google_url_logout
36
+ : authConfig?.passport?.local_url_logout,
37
+ socket: {
38
+ enabled: socketConfig?.enabled === true,
39
+ url: socketConfig?.config?.cli?.url || null,
40
+ options: socketConfig?.config?.cli?.options || {}
41
+ }
29
42
  }
43
+ ctx.miolo.logger.debug(
44
+ `[render-ssr] rendering an ${ctx?.session?.authenticated === true ? "authenticated" : "unauthenticated"} context...`
45
+ )
30
46
 
31
47
  const ssr_data = await ssr_loader(ctx)
32
48
  const context = ssr_build_context(ctx, config, ssr_data)
33
49
  const rendered_html = await ssr_html_renderer(ctx, context)
34
-
35
- ctx.miolo.logger.debug(`[render-ssr] Returned body is ${Buffer.byteLength(rendered_html, 'utf8')} bytes`)
36
50
 
37
- ctx.type = 'text/html'
38
- ctx.body= rendered_html
39
- ctx.status = 200
40
- } catch(e) {
51
+ ctx.miolo.logger.debug(
52
+ `[render-ssr] Returned body is ${Buffer.byteLength(rendered_html, "utf8")} bytes`
53
+ )
41
54
 
55
+ ctx.type = "text/html"
56
+ ctx.body = rendered_html
57
+ ctx.status = 200
58
+ } catch (e) {
42
59
  ctx.body = e.stack
43
- ctx.type = 'text/html'
60
+ ctx.type = "text/html"
44
61
  ctx.status = 500
45
62
  }
46
63
  }
@@ -1,27 +1,46 @@
1
- import koa_mount from 'koa-mount'
2
- import koa_serve from 'koa-static'
3
- import koa_favicon from 'koa-favicon'
4
- import {existsSync} from 'fs'
1
+ import { existsSync } from "node:fs"
2
+ import koa_favicon from "koa-favicon"
3
+ import koa_mount from "koa-mount"
4
+ import koa_serve from "koa-static"
5
5
 
6
- const init_static_middleware = ( app, config ) => {
7
- const {favicon, folders} = config
6
+ const init_static_middleware = (app, config) => {
7
+ const { favicon, folders, headers } = config
8
8
 
9
9
  if (favicon && existsSync(favicon)) {
10
10
  app.context.miolo.logger.debug(`[static] Serving favicon from -${favicon}-`)
11
11
  app.use(koa_favicon(favicon))
12
12
  } else {
13
- app.context.miolo.logger.warn(`[static] Cannot serve favicon from -${favicon}- (does not exist)`)
13
+ app.context.miolo.logger.warn(
14
+ `[static] Cannot serve favicon from -${favicon}- (does not exist)`
15
+ )
14
16
  }
15
-
16
- for(const [froute, fpath] of Object.entries(folders)) {
17
+
18
+ // Do not cache some specific files
19
+ app.use(async (ctx, next) => {
20
+ for (const [fpath, fheaders] of Object.entries(headers)) {
21
+ if (ctx.path === fpath) {
22
+ app.context.miolo.logger.info(
23
+ `[static] Setting headers for -${fpath}- ${Object.keys(fheaders).join(", ")}`
24
+ )
25
+ for (const [key, value] of Object.entries(fheaders)) {
26
+ ctx.set(key, value)
27
+ }
28
+ break
29
+ }
30
+ }
31
+ await next()
32
+ })
33
+
34
+ for (const [froute, fpath] of Object.entries(folders)) {
17
35
  if (fpath && existsSync(fpath)) {
18
- app.context.miolo.logger.debug(`[static] Mounting static folder ${froute} => -${fpath}-`)
19
- app.use(koa_mount(froute, koa_serve(fpath, {index: false})))
36
+ app.context.miolo.logger.info(`[static] Mounting static folder ${froute} => -${fpath}-`)
37
+ app.use(koa_mount(froute, koa_serve(fpath, { index: false })))
20
38
  } else {
21
- app.context.miolo.logger.warn(`[static] Cannot mount static folder ${froute} => -${fpath}- (does not exist)`)
22
-
39
+ app.context.miolo.logger.warn(
40
+ `[static] Cannot mount static folder ${froute} => -${fpath}- (does not exist)`
41
+ )
23
42
  }
24
43
  }
25
44
  }
26
45
 
27
- export {init_static_middleware}
46
+ export { init_static_middleware }