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
@@ -0,0 +1,132 @@
1
+ import { intre_now } from "intre"
2
+ import { sha512 } from "#server/utils/crypt.mjs"
3
+
4
+ const _USER_LAST_CONN_DELAY = 5 * 1000
5
+ let _USER_LAST_CONN_UPDATE = 0
6
+ const _should_update_user_last_conn = () => {
7
+ const now = Math.floor(Date.now() / 1000)
8
+ if (_USER_LAST_CONN_UPDATE + _USER_LAST_CONN_DELAY < now) {
9
+ _USER_LAST_CONN_UPDATE = now
10
+ return true
11
+ }
12
+ return false
13
+ }
14
+
15
+ export async function db_find_user_by_id(miolo, uid) {
16
+ miolo.logger.silly(`[db_find_user_by_id] uid: ${uid}`)
17
+
18
+ const conn = await miolo.db.get_connection()
19
+ // TODO : handle transactions
20
+ const options = { transaction: undefined }
21
+
22
+ const query = `
23
+ SELECT id, username, name, email, active, google_id, google_picture,
24
+ last_login_date, last_login_ip, login_count, last_conn_at, created_at, last_update_at
25
+ FROM account
26
+ WHERE id = $1`
27
+
28
+ const data = await conn.select(query, [uid], options)
29
+
30
+ if (data.length === 0) {
31
+ return undefined
32
+ }
33
+
34
+ let user
35
+
36
+ try {
37
+ user = data[0]
38
+
39
+ if (_should_update_user_last_conn()) {
40
+ miolo.logger.silly(`[db_find_user_by_id] Updating ${user.name} last conn`)
41
+ const qupd = "UPDATE account SET last_conn_at = $1 WHERE id = $2"
42
+ conn.execute(qupd, [intre_now(), uid])
43
+ }
44
+ } catch (e) {
45
+ miolo.logger.error(`[db_find_user_by_id] Error ${e}`)
46
+ }
47
+
48
+ miolo.logger.silly(`[db_find_user_by_id] Found ${user.username}`)
49
+
50
+ return user
51
+ }
52
+
53
+ export async function db_auth_user(miolo, username, password) {
54
+ miolo.logger.debug(`[db_auth_user] authing: ${username}`)
55
+
56
+ const conn = await miolo.db.get_connection()
57
+ // TODO : handle transactions
58
+ const options = { transaction: undefined }
59
+
60
+ let data
61
+ let msg = "Credenciales incorrectas"
62
+
63
+ const query = `
64
+ SELECT id, password
65
+ FROM account
66
+ WHERE username = $1`
67
+
68
+ const ruser = await conn.selectOne(query, [username], options)
69
+
70
+ if (ruser?.id === undefined) {
71
+ msg = "Usuario inexistente"
72
+ } else {
73
+ if (ruser?.password !== sha512(password, process.env.MIOLO_SESSION_SALT)) {
74
+ msg = "Contraseña incorrecta"
75
+ } else if (!Number.isNaN(ruser?.id)) {
76
+ data = await db_find_user_by_id(miolo, ruser.id)
77
+ msg = undefined
78
+
79
+ // update last conn
80
+ miolo.logger.silly(`[db_auth_user] updating ${username} last* fields`)
81
+ const ip = ""
82
+ const log_count = data?.login_count || 0
83
+ const qupd =
84
+ "UPDATE account SET last_conn_at = $1, last_login_date = $1, last_login_ip = $2, login_count = $3 WHERE id = $4"
85
+ conn.execute(qupd, [intre_now(), ip, log_count + 1, ruser.id])
86
+ }
87
+ }
88
+
89
+ miolo.logger.debug(`[db_auth_user] authing: ${username} => ${msg || "ok"}`)
90
+
91
+ return [data, msg]
92
+ }
93
+
94
+ export async function db_user_find_or_create_from_google(
95
+ miolo,
96
+ email,
97
+ name,
98
+ google_id,
99
+ google_picture
100
+ ) {
101
+ miolo.logger.debug(`[db_user_find_or_create_from_google] checking: ${email}`)
102
+ const conn = await miolo.db.get_connection()
103
+ // TODO : handle transactions
104
+ const options = { transaction: undefined }
105
+
106
+ const query = `
107
+ SELECT id, username, name, email, active, google_id, google_picture,
108
+ last_login_date, last_login_ip, login_count, last_conn_at, created_at, last_update_at
109
+ FROM account
110
+ WHERE google_id = $1`
111
+
112
+ const ruser = await conn.selectOne(query, [google_id], options)
113
+
114
+ if (ruser?.id === undefined) {
115
+ miolo.logger.debug(`[db_user_find_or_create_from_google] user ${email} not found, creating`)
116
+ const UUser = await conn.get_model("account")
117
+
118
+ const data = {
119
+ name,
120
+ email,
121
+ google_id,
122
+ google_picture,
123
+ active: 1
124
+ }
125
+ const uid = await UUser.insert(data, options)
126
+ data.id = uid
127
+ return [data, undefined]
128
+ } else {
129
+ miolo.logger.debug(`[db_user_find_or_create_from_google] user ${email} found, returning`)
130
+ return [ruser, undefined]
131
+ }
132
+ }
@@ -0,0 +1,38 @@
1
+ import { sha512 } from "#server/utils/crypt.mjs"
2
+
3
+ export async function db_password_change(miolo, email, passwords) {
4
+ const conn = await miolo.db.get_connection()
5
+ // TODO : handle transactions
6
+ const options = { transaction: undefined }
7
+
8
+ let ok = false
9
+ let msg = "Credenciales incorrectas"
10
+
11
+ let query = `
12
+ SELECT id, password
13
+ FROM account
14
+ WHERE username = $1 OR email = $1`
15
+
16
+ const data = await conn.selectOne(query, [email], options)
17
+
18
+ if (Number.isNaN(data?.id)) {
19
+ msg = "Usuario inexistente"
20
+ } else {
21
+ if (data?.password !== sha512(passwords.current, process.env.MIOLO_SESSION_SALT)) {
22
+ msg = "Contraseña actual incorrecta"
23
+ } else {
24
+ try {
25
+ query = "UPDATE account SET password = $1 WHERE id = $2"
26
+ const pwd = sha512(passwords.new, process.env.MIOLO_SESSION_SALT)
27
+ await conn.execute(query, [pwd, data.id])
28
+
29
+ msg = "Contraseña modificada correctamente"
30
+ ok = true
31
+ } catch (err) {
32
+ msg = `Error al modificar la contraseña: ${err}`
33
+ }
34
+ }
35
+ }
36
+
37
+ return { ok, msg }
38
+ }
@@ -0,0 +1,17 @@
1
+ export async function db_user_save(miolo, data) {
2
+ const conn = await miolo.db.get_connection()
3
+ // TODO : handle transactions
4
+ const options = { transaction: undefined }
5
+
6
+ const uid = data.id
7
+ const User = await conn.get_model("account")
8
+
9
+ let nuid = uid
10
+ if (uid === undefined) {
11
+ nuid = await User.insert(data, options)
12
+ } else {
13
+ data.email = data.username
14
+ await User.update(data, { id: uid }, options)
15
+ }
16
+ return nuid
17
+ }
@@ -0,0 +1,15 @@
1
+ import { db_auth_user } from "#server/io/db/users/auth.mjs"
2
+
3
+ const local_auth_user = async (username, password, ctx) => {
4
+ const [user, msg] = await db_auth_user(ctx.miolo, username, password)
5
+
6
+ return [user, msg]
7
+ }
8
+
9
+ export default {
10
+ basic: {
11
+ auth_user: local_auth_user,
12
+ realm: undefined, //'demo.app',
13
+ paths: ["/crud"] // ['/api']
14
+ }
15
+ }
@@ -0,0 +1,3 @@
1
+ export default {
2
+ guest: {}
3
+ }
@@ -0,0 +1,73 @@
1
+ import {
2
+ db_auth_user,
3
+ db_find_user_by_id,
4
+ db_user_find_or_create_from_google
5
+ } from "#server/io/db/users/auth.mjs"
6
+
7
+ const get_user_id = async (user, ctx) => {
8
+ ctx.miolo.logger.debug(
9
+ `[auth][local] get_user_id() - name ${user?.name} - sessionId ${ctx.sessionId}`
10
+ )
11
+ return user?.id
12
+ }
13
+
14
+ const find_user_by_id = async (id, ctx) => {
15
+ ctx.miolo.logger.debug(
16
+ `[auth][local] find_user_by_id() - Searching user id ${id} - sessionId ${ctx.sessionId}`
17
+ )
18
+ const user = await db_find_user_by_id(ctx.miolo, id)
19
+ return user
20
+ }
21
+
22
+ const local_auth_user = async (username, password, ctx) => {
23
+ ctx.miolo.logger.debug(
24
+ `[auth][local] local_auth_user() - checking credentials for ${username} - sessionId ${ctx.sessionId}`
25
+ )
26
+
27
+ const [user, msg] = await db_auth_user(ctx.miolo, username, password)
28
+ return [user, msg]
29
+ }
30
+
31
+ const google_auth_user = async (_accessToken, _refreshToken, profile, ctx) => {
32
+ try {
33
+ // Extract Google profile info
34
+ const google_id = profile.id
35
+ const email = profile.emails?.[0]?.value
36
+ const name = profile.displayName
37
+ const google_picture = profile.photos?.[0]?.value
38
+
39
+ ctx.miolo.logger.info(`[auth][google] Authenticated user: ${email}`)
40
+ ctx.miolo.logger.debug(
41
+ `[auth][google] google_auth_user() - upsaving db user for ${email} - sessionId ${ctx.sessionId}`
42
+ )
43
+
44
+ const [user, msg] = await db_user_find_or_create_from_google(
45
+ ctx.miolo,
46
+ email,
47
+ name,
48
+ google_id,
49
+ google_picture
50
+ )
51
+ return [user, msg]
52
+ } catch (error) {
53
+ ctx.miolo.logger.error(`[auth][google] Error preparing db user: ${error}`)
54
+ return [undefined, error]
55
+ }
56
+ }
57
+
58
+ export default {
59
+ get_user_id,
60
+ find_user_by_id,
61
+ local_auth_user,
62
+ local_url_login: "/login",
63
+ local_url_logout: "/logout",
64
+ local_url_login_redirect: undefined,
65
+ local_url_logout_redirect: "/",
66
+ google_auth_user,
67
+ google_client_id: process.env.MIOLO_AUTH_GOOGLE_CLIENT_ID,
68
+ google_client_secret: process.env.MIOLO_AUTH_GOOGLE_CLIENT_SECRET,
69
+ google_url_login: "/auth/google",
70
+ google_url_callback: process.env.MIOLO_AUTH_GOOGLE_CALLBACK_URL || "/auth/google/callback",
71
+ google_url_logout: "/logout",
72
+ google_url_logout_redirect: "/"
73
+ }
@@ -0,0 +1,11 @@
1
+ export default {
2
+ default: {
3
+ type: "redis",
4
+ redis: {
5
+ host: process.env.IS_DOCKER === "true" ? "redis" : "127.0.0.1",
6
+ port: 6379
7
+ },
8
+ version: 2,
9
+ clean: false
10
+ }
11
+ }
@@ -0,0 +1,7 @@
1
+ import { blue } from "tinguir"
2
+
3
+ export const onTickFoo = async (miolo) => {
4
+ miolo.logger.info(`${blue("[cron][foo]")} Fooing...`)
5
+
6
+ miolo.logger.info(`${blue("[cron][foo]")} Foo done!`)
7
+ }
@@ -0,0 +1,28 @@
1
+ import { onTickFoo } from "./foo.mjs"
2
+ import { cacheInvalidate } from "./invalidate.mjs"
3
+
4
+ // check https://github.com/kelektiv/node-cron#readme
5
+ //
6
+ // https://crontab.guru/
7
+
8
+ export default function init_cron() {
9
+ const cronList = []
10
+
11
+ cronList.push({
12
+ name: "miolo-sample-foo",
13
+ cronTime: "*/30 * * * *",
14
+ onTick: onTickFoo,
15
+ start: true
16
+ })
17
+
18
+ cronList.push({
19
+ name: "miolo-sample-cache-invalidate",
20
+ cronTime: "*/1 * * * *",
21
+ onTick: cacheInvalidate,
22
+ start: true
23
+ })
24
+
25
+ console.log(`[miolo-sample][cron] ${cronList.length} custom cron jobs loaded`)
26
+
27
+ return cronList
28
+ }
@@ -0,0 +1,21 @@
1
+ import { intre_now, intre_to_str } from "intre"
2
+ import { blue } from "tinguir"
3
+ import { db_todo_upsave } from "#server/io/db/todos/upsave.mjs"
4
+
5
+ export const cacheInvalidate = async (miolo) => {
6
+ miolo.logger.info(`${blue("[cron][cache_invalidate]")} Invalidating cache...`)
7
+
8
+ // broadcast an event to all clients
9
+ //miolo.io.emit("ssr-invalidate", { name: "todos" })
10
+
11
+ const ctx = { miolo }
12
+
13
+ await db_todo_upsave(ctx, {
14
+ description: `${intre_to_str(intre_now(), "DD/MM/YYYY HH:mm:ss")} - auto todo`,
15
+ done: false
16
+ })
17
+
18
+ miolo.io.emit("ssr-refresh", { name: "todos" })
19
+
20
+ miolo.logger.info(`${blue("[cron][cache_invalidate]")} Cache invalidated!`)
21
+ }
@@ -0,0 +1,36 @@
1
+ // import path from 'path'
2
+ // import {fileURLToPath} from 'url'
3
+ import { beforeInsertUser } from "#server/io/db/triggers/user.mjs"
4
+
5
+ // const __filename = fileURLToPath(import.meta.url)
6
+ // const __dirname = path.dirname(__filename)
7
+
8
+ // Only for CRUD routes!
9
+ const useUserFields = {
10
+ use: true,
11
+ fieldNames: {
12
+ created_by: "created_by",
13
+ last_update_by: "last_update_by"
14
+ }
15
+ }
16
+
17
+ const TABLES = [
18
+ {
19
+ name: "account",
20
+ useDateFields: true,
21
+ triggers: {
22
+ beforeInsert: beforeInsertUser
23
+ }
24
+ },
25
+ {
26
+ name: "todo",
27
+ useDateFields: true,
28
+ useUserFields
29
+ }
30
+ ]
31
+
32
+ export default {
33
+ options: {
34
+ tables: TABLES
35
+ }
36
+ }
@@ -0,0 +1,14 @@
1
+ import path from "node:path"
2
+
3
+ const root = (dir) => path.resolve(process.cwd(), dir)
4
+
5
+ export default {
6
+ static: {
7
+ favicon: root("src/static/img/favicon.ico"),
8
+ folders: {
9
+ "/build": root("build"),
10
+ "/static": root("src/static"),
11
+ "/": root("src/static/public")
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,43 @@
1
+ import passport from "./auth/passport.mjs"
2
+ import cache from "./cache.mjs"
3
+ import init_cron from "./cron/index.mjs"
4
+ import db from "./db.mjs"
5
+ import http from "./http.mjs"
6
+ import routes from "./routes/index.mjs"
7
+ import { loader } from "./ssr/loader.mjs"
8
+
9
+ export default () => {
10
+ return {
11
+ name: "miolo-sample",
12
+ http,
13
+ auth: {
14
+ passport
15
+ },
16
+ db,
17
+ routes,
18
+ cache,
19
+ build: {
20
+ ssr: { loader }
21
+ },
22
+ cron: init_cron(),
23
+ socket: {
24
+ enabled: true,
25
+ namespaces: [
26
+ {
27
+ name: "todos-update",
28
+ listener: (data) => {
29
+ console.log("TODOS UPDATED!!!")
30
+ console.log(data)
31
+ }
32
+ },
33
+ {
34
+ name: "ping",
35
+ listener: (data) => {
36
+ console.log("PING!!!")
37
+ console.log(data)
38
+ }
39
+ }
40
+ ]
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,16 @@
1
+ export default [
2
+ {
3
+ prefix: "/crud",
4
+ auth: {
5
+ require: true,
6
+ action: "redirect",
7
+ redirect_url: "/login"
8
+ },
9
+ routes: [
10
+ {
11
+ name: "todo",
12
+ mode: "rw"
13
+ }
14
+ ]
15
+ }
16
+ ]
@@ -0,0 +1,8 @@
1
+ import queries from "#server/routes/index.mjs"
2
+ import crud from "./crud.mjs"
3
+
4
+ export default {
5
+ bodyField: undefined,
6
+ crud: crud,
7
+ queries: queries
8
+ }
@@ -0,0 +1,13 @@
1
+ import { AppServer } from "miolo-react"
2
+ import { renderToString } from "react-dom/server"
3
+ import { StaticRouter } from "react-router"
4
+ import App from "../../../cli/App.jsx"
5
+
6
+ export const render = (ctx, context) =>
7
+ renderToString(
8
+ <AppServer context={context}>
9
+ <StaticRouter location={ctx.url}>
10
+ <App />
11
+ </StaticRouter>
12
+ </AppServer>
13
+ )
@@ -0,0 +1,18 @@
1
+ import { db_todo_read } from "#server/io/db/todos/read.mjs"
2
+
3
+ const loader = async (ctx) => {
4
+ let lastTodos = []
5
+
6
+ try {
7
+ lastTodos = await db_todo_read(ctx, { options: { limit: 3 } })
8
+ lastTodos = lastTodos.sort((a, b) => b.created_at - a.created_at)
9
+ } catch (_) {}
10
+
11
+ const data = {
12
+ lastTodos
13
+ }
14
+
15
+ return data
16
+ }
17
+
18
+ export { loader }
@@ -0,0 +1,66 @@
1
+ import Joi from "joi"
2
+ import { with_miolo_input_schema, with_miolo_output_schema } from "miolo"
3
+ import { r_todo_delete, r_todo_toggle_done, r_todo_upsave } from "./todos/mod.mjs"
4
+
5
+ import { r_todo_find, r_todo_last, r_todo_list } from "./todos/read.mjs"
6
+ import { r_todo_count_last_hours, r_todo_insert_fake } from "./todos/special.mjs"
7
+ import { r_change_password, r_forgot, r_user_save } from "./users/user.mjs"
8
+
9
+ const auth = {
10
+ require: true,
11
+ action: "redirect",
12
+ redirect_url: "/page/login"
13
+ }
14
+
15
+ export default [
16
+ {
17
+ prefix: "api",
18
+ routes: [
19
+ { method: "POST", url: "/user/forgot", callback: r_forgot },
20
+ { method: "POST", url: "/user/chpwd", auth, callback: r_change_password },
21
+ { method: "POST", url: "/user/save", auth, callback: r_user_save },
22
+
23
+ { method: "GET", url: "/todo/list", auth, callback: r_todo_list },
24
+ { method: "GET", url: "/todo/last", auth, callback: r_todo_last },
25
+ { method: "GET", url: "/todo/findone", auth, callback: r_todo_find },
26
+ { method: "POST", url: "/todo/upsave", auth, callback: r_todo_upsave },
27
+ { method: "POST", url: "/todo/delete", auth, callback: r_todo_delete },
28
+ { method: "POST", url: "/todo/toggle", auth, callback: r_todo_toggle_done },
29
+
30
+ {
31
+ url: "/todo/last_hours",
32
+ method: "GET",
33
+ // Passing schema on the route definition
34
+ callback: r_todo_count_last_hours,
35
+ schema: {
36
+ input: Joi.object({
37
+ hours: Joi.number().min(1).max(24)
38
+ }),
39
+ output: Joi.object({
40
+ count: Joi.number()
41
+ })
42
+ }
43
+ },
44
+ {
45
+ url: "/todo/fake",
46
+ method: "POST",
47
+ // Wrapping function with the schema
48
+ callback: with_miolo_output_schema(
49
+ with_miolo_input_schema(
50
+ r_todo_insert_fake,
51
+ Joi.object({
52
+ done: Joi.bool().optional().default(false)
53
+ })
54
+ ),
55
+ Joi.object({
56
+ ok: Joi.bool(),
57
+ data: Joi.object({
58
+ id: Joi.number()
59
+ })
60
+ })
61
+ ),
62
+ auth
63
+ }
64
+ ]
65
+ }
66
+ ]
@@ -0,0 +1,52 @@
1
+ import { db_todo_delete } from "#server/io/db/todos/delete.mjs"
2
+ import { db_todo_toggle } from "#server/io/db/todos/toggle.mjs"
3
+ import { db_todo_upsave } from "#server/io/db/todos/upsave.mjs"
4
+
5
+ export async function r_todo_upsave(ctx, params) {
6
+ try {
7
+ ctx.miolo.logger.info(
8
+ `[r_todo_upsave] Upsaving todo info for tid ${params?.id || "new"} -> ${params?.description}`
9
+ )
10
+
11
+ const res = await db_todo_upsave(ctx, params)
12
+
13
+ ctx.miolo.logger.info(`[r_todo_upsave] Upsaved todo info for tid ${params?.id || "new"}`)
14
+ return { ok: true, data: res }
15
+ } catch (error) {
16
+ ctx.miolo.logger.error(
17
+ `[r_todo_upsave] Error upsaving todo info for tid ${params?.id || "new"}: ${error}`
18
+ )
19
+ return { ok: false, error: error?.message }
20
+ }
21
+ }
22
+
23
+ export async function r_todo_delete(ctx, params) {
24
+ try {
25
+ ctx.miolo.logger.info(`[r_todo_delete] Deleting todo info for tid ${params?.id}`)
26
+
27
+ const res = await db_todo_delete(ctx, params)
28
+
29
+ ctx.miolo.logger.info(`[r_todo_delete] Deleted todo for tid ${params.id}`)
30
+ return { ok: true, data: res }
31
+ } catch (error) {
32
+ ctx.miolo.logger.error(`[r_todo_delete] Error deleting todo for tid ${params?.id}: ${error}`)
33
+ return { ok: false, error: error?.message }
34
+ }
35
+ }
36
+
37
+ export async function r_todo_toggle_done(ctx, params) {
38
+ try {
39
+ ctx.miolo.logger.info(`[r_todo_toggle_done] Toggling todo with id ${params?.id}`)
40
+
41
+ const done = await db_todo_toggle(ctx, params)
42
+
43
+ ctx.miolo.logger.info(`[r_todo_toggle_done] Toggled todo with tid ${params?.id}`)
44
+
45
+ return { ok: true, data: { done } }
46
+ } catch (error) {
47
+ ctx.miolo.logger.error(
48
+ `[r_todo_toggle_done] Error toggling todo with tid ${params?.id}: ${error}`
49
+ )
50
+ return { ok: false, error: error?.message }
51
+ }
52
+ }
@@ -0,0 +1,45 @@
1
+ import { db_todo_find } from "#server/io/db/todos/find.mjs"
2
+ import { db_todo_read } from "#server/io/db/todos/read.mjs"
3
+
4
+ export async function r_todo_list(ctx, _params) {
5
+ try {
6
+ ctx.miolo.logger.info(`[r_todo_list] Reading todo list`)
7
+
8
+ const res = await db_todo_read(ctx, { filter: {}, options: {} })
9
+
10
+ ctx.miolo.logger.info(`[r_todo_list] Read todo list (${res.length})`)
11
+ return { ok: true, data: res }
12
+ } catch (error) {
13
+ ctx.miolo.logger.error(`[r_todo_list] Error reading todo list: ${error}`)
14
+ return { ok: false, error: error?.message }
15
+ }
16
+ }
17
+
18
+ // biome-ignore lint/correctness/noUnusedFunctionParameters: params is not used
19
+ export async function r_todo_last(ctx, params) {
20
+ try {
21
+ ctx.miolo.logger.info(`[r_todo_last] Reading last todos`)
22
+
23
+ const res = await db_todo_read(ctx, { options: { limit: 3 } })
24
+
25
+ ctx.miolo.logger.info(`[r_todo_last] Read last todos (${res.length})`)
26
+ return { ok: true, data: res }
27
+ } catch (error) {
28
+ ctx.miolo.logger.error(`[r_todo_last] Error reading last todos: ${error}`)
29
+ return { ok: false, error: error?.message }
30
+ }
31
+ }
32
+
33
+ export async function r_todo_find(ctx, params) {
34
+ try {
35
+ ctx.miolo.logger.info(`[r_todo_find] Reading todo for tid ${params?.id}`)
36
+
37
+ const todo = await db_todo_find(ctx, { filter: { id: params?.id } })
38
+
39
+ ctx.miolo.logger.info(`[r_todo_find] Read todo for tid ${params?.id}`)
40
+ return { ok: todo?.id !== undefined, data: todo }
41
+ } catch (error) {
42
+ ctx.miolo.logger.error(`[r_todo_find] Error reading todo for tid ${params?.id}: ${error}`)
43
+ return { ok: false, error: error?.message }
44
+ }
45
+ }