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,191 @@
1
+ import { useMioloContext } from "miolo-react"
2
+ import { useCallback, useEffect, useState } from "react"
3
+ import useSessionContext from "#cli/context/session/useSessionContext.mjs"
4
+ import useUIContext from "#cli/context/ui/useUIContext.mjs"
5
+ import TodoList from "#ns/models/TodoList.mjs"
6
+ import TodosContext from "./TodosContext.jsx"
7
+
8
+ const TodosProvider = ({ children }) => {
9
+ // const [status, setStatus] = useState("loaded")
10
+ const { logger } = useMioloContext()
11
+ const { useSsrData, fetcher, socket, authenticated } = useSessionContext()
12
+ const { toast } = useUIContext()
13
+ const [useCrud, setUseCrud] = useState(true)
14
+ const [socketInited, setSocketInited] = useState(false)
15
+
16
+ const {
17
+ data: todoList,
18
+ setData: setTodoList,
19
+ refresh: refreshTodoList,
20
+ ready,
21
+ invalidate: invalidateTodoList
22
+ } = useSsrData("todos", {
23
+ model: TodoList,
24
+ loader: useCallback(
25
+ async (_context, fetcher) => {
26
+ //setStatus("loading")
27
+ const res = useCrud ? await fetcher.read("/crud/todo") : await fetcher.get("/api/todo/list")
28
+
29
+ if (!res.ok) {
30
+ toast.error(`Error loading todos: ${res.error}`)
31
+ }
32
+ const data = res?.data || []
33
+ //setStatus("loaded")
34
+ return data.sort((a, b) => b.created_at - a.created_at)
35
+ },
36
+ [useCrud, toast]
37
+ ),
38
+ cache: true
39
+ })
40
+
41
+ const addTodo = useCallback(
42
+ (text) => {
43
+ async function addIt() {
44
+ const todoObject = {
45
+ id: undefined,
46
+ description: text,
47
+ done: false
48
+ }
49
+
50
+ const res = useCrud
51
+ ? await fetcher.upsave("crud/todo", todoObject)
52
+ : await fetcher.post("/api/todo/upsave", todoObject)
53
+
54
+ if (!res.ok) {
55
+ toast.error(`Error adding todo: ${res.error}`)
56
+ }
57
+
58
+ todoObject.id = useCrud ? res?.data : res?.data?.id
59
+
60
+ setTodoList([todoObject, ...todoList.getData()])
61
+ }
62
+
63
+ addIt()
64
+ },
65
+ [fetcher, todoList, setTodoList, useCrud, toast]
66
+ )
67
+
68
+ const toggleTodo = useCallback(
69
+ (todoId) => {
70
+ async function toggleIt() {
71
+ const nTodoList = [...todoList.getData()]
72
+ const selectedTodoIndex = nTodoList.findIndex((item) => item.id === todoId)
73
+ nTodoList[selectedTodoIndex].done = !nTodoList[selectedTodoIndex].done
74
+
75
+ setTodoList(nTodoList)
76
+
77
+ const res = useCrud
78
+ ? await fetcher.upsave("crud/todo", nTodoList[selectedTodoIndex])
79
+ : await fetcher.post("/api/todo/toggle", {
80
+ id: todoId,
81
+ done: nTodoList[selectedTodoIndex].done
82
+ })
83
+
84
+ if (!res.ok) {
85
+ toast.error(`Error toggling todo: ${res.error}`)
86
+ }
87
+ }
88
+
89
+ toggleIt()
90
+ },
91
+ [fetcher, todoList, setTodoList, useCrud, toast]
92
+ )
93
+
94
+ const removeTodo = useCallback(
95
+ async (todoId) => {
96
+ const nTodoList = [...todoList.getData()]
97
+ nTodoList.splice(
98
+ nTodoList.findIndex((item) => item.id === todoId),
99
+ 1
100
+ )
101
+
102
+ setTodoList(nTodoList)
103
+
104
+ const res = useCrud
105
+ ? await fetcher.remove("crud/todo", todoId)
106
+ : await fetcher.post("api/todo/delete", { id: todoId })
107
+
108
+ if (!res.ok) {
109
+ toast.error(`Error removing todo: ${res.error}`)
110
+ } else {
111
+ toast.info(`Todo removed successfully`)
112
+ }
113
+ },
114
+ [fetcher, todoList, setTodoList, useCrud, toast]
115
+ )
116
+
117
+ const checkLastHours = useCallback(
118
+ async ({ hours }) => {
119
+ const res = await fetcher.get("api/todo/last_hours", { hours })
120
+ if (res.ok === true) {
121
+ toast.info(`You have added ${res?.data?.count} todos in the last ${hours} hours`)
122
+ } else {
123
+ toast.error(`Error checking last hours: ${res.error}`)
124
+ }
125
+ },
126
+ [fetcher, toast]
127
+ )
128
+
129
+ const insertFakeTodo = useCallback(async () => {
130
+ const res = await fetcher.post("api/todo/fake", { done: true })
131
+
132
+ if (!res.ok) {
133
+ toast.error(`Error adding fake todo: ${res.error}`)
134
+ } else {
135
+ toast.info(`Fake todo added with id ${res?.data?.id}`)
136
+ }
137
+ refreshTodoList()
138
+ }, [fetcher, refreshTodoList, toast])
139
+
140
+ const pingSocket = useCallback(() => {
141
+ if (!socket) {
142
+ toast.error("Socket not initialized")
143
+ return
144
+ }
145
+ socket.emit("ping", { timestamp: new Date().toISOString() })
146
+ }, [socket, toast])
147
+
148
+ useEffect(() => {
149
+ if (socket === undefined) {
150
+ return
151
+ }
152
+
153
+ if (socketInited) {
154
+ return
155
+ }
156
+ setSocketInited(true)
157
+
158
+ socket.on("connect", () => {
159
+ logger.info("Connected to server!")
160
+ })
161
+
162
+ socket.on("todos-update", (data) => {
163
+ logger.info("TODOS UPDATED!!!")
164
+ logger.info(data)
165
+ })
166
+ }, [socket, socketInited, logger])
167
+
168
+ return (
169
+ <TodosContext.Provider
170
+ value={{
171
+ todoList,
172
+ refreshTodoList,
173
+ loading: !ready,
174
+ loaded: ready,
175
+ addTodo,
176
+ toggleTodo,
177
+ removeTodo,
178
+ checkLastHours,
179
+ insertFakeTodo,
180
+ canEdit: authenticated,
181
+ useCrud,
182
+ setUseCrud,
183
+ pingSocket
184
+ }}
185
+ >
186
+ {children}
187
+ </TodosContext.Provider>
188
+ )
189
+ }
190
+
191
+ export default TodosProvider
@@ -0,0 +1,4 @@
1
+ import { useContext } from "react"
2
+ import TodosContext from "./TodosContext.jsx"
3
+
4
+ export default () => useContext(TodosContext)
@@ -0,0 +1,29 @@
1
+ import { MioloModel } from "miolo-model"
2
+
3
+ export default class Todo extends MioloModel {
4
+ get id() {
5
+ return this._get("id", null)
6
+ }
7
+ get description() {
8
+ return this._get("description", "")
9
+ }
10
+ get done() {
11
+ return this._get("done", false)
12
+ }
13
+ toggle() {
14
+ this._set("done", !this.done)
15
+ }
16
+
17
+ get createdAt() {
18
+ return this._get("created_at")
19
+ }
20
+ get lastUpdateAt() {
21
+ return this._get("last_update_at")
22
+ }
23
+ get createdBy() {
24
+ return this._get("created_by")
25
+ }
26
+ get lastUpdateBy() {
27
+ return this._get("last_update_by")
28
+ }
29
+ }
@@ -0,0 +1,8 @@
1
+ import { MioloArray } from "miolo-model"
2
+ import Todo from "./Todo.mjs"
3
+
4
+ export default class TodoList extends MioloArray {
5
+ constructor(items = []) {
6
+ super(Todo, items)
7
+ }
8
+ }
@@ -0,0 +1,40 @@
1
+ import { MioloModel } from "miolo-model"
2
+
3
+ export default class User extends MioloModel {
4
+ get id() {
5
+ return this._get("id")
6
+ }
7
+ get username() {
8
+ return this._get("username")
9
+ }
10
+ get password() {
11
+ return this._get("password")
12
+ }
13
+ get name() {
14
+ return this._get("name")
15
+ }
16
+ get email() {
17
+ return this._get("email")
18
+ }
19
+ get active() {
20
+ return this._get("active")
21
+ }
22
+ get lastLoginDate() {
23
+ return this._get("last_login_date")
24
+ }
25
+ get lastLoginIp() {
26
+ return this._get("last_login_ip")
27
+ }
28
+ get loginCount() {
29
+ return this._get("login_count")
30
+ }
31
+ get lastConnAt() {
32
+ return this._get("last_conn_at")
33
+ }
34
+ get createdAt() {
35
+ return this._get("created_at")
36
+ }
37
+ get lastUpdateAt() {
38
+ return this._get("last_update_at")
39
+ }
40
+ }
@@ -0,0 +1,10 @@
1
+ import { intre_now, intre_pretty_long } from "intre"
2
+
3
+ function check_today() {
4
+ const now = intre_now()
5
+ const today = intre_pretty_long(now)
6
+ console.log(`[miolo-sample][check_today] Today is ${today}`)
7
+ return today
8
+ }
9
+
10
+ check_today()
@@ -0,0 +1,21 @@
1
+ export async function cache_get_or_set_json(ctx, name, key, callback) {
2
+ const cache = await ctx.miolo.cache.get_cache(name)
3
+
4
+ if (!cache) {
5
+ ctx.miolo.logger.warn(`[cache] Cache ${name} is not ready!`)
6
+ }
7
+
8
+ if (cache) {
9
+ if (await cache.hasItem(key)) {
10
+ return JSON.parse(await cache.getItem(key))
11
+ }
12
+ }
13
+
14
+ const data = await callback()
15
+
16
+ if (cache) {
17
+ await cache.setItem(key, JSON.stringify(data))
18
+ }
19
+
20
+ return data
21
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ *
3
+ * @param {*} filter An object like
4
+ * {field1: 10, field2: 'HEY', field3: [1, 2, 3], ...}
5
+ * @param {*} modifiers Optionally modify how fields will be filtered
6
+ * {
7
+ * <field_name>: {alias: '', coalesce: true|false|'', op: =|~*|...
8
+ * }
9
+ * alias: the alias in the query for that filter ('s.id', 'table.field1', ...)
10
+ * <field_name> will be the default
11
+ * coalesce: if you want to apply some COALESCE on the filter.
12
+ * For example, if true, result of filter is:
13
+ * ' AND COALESCE(<alias>, TRUE) IS <value> '
14
+ * op: By default. the operator to use will be taken based on the value's type:
15
+ * If array : ' AND <alias> IN ($1:CSV) '
16
+ * If boolean: ' AND <alias> IS TRUE/FALSE '
17
+ * If others : ' AND <alias> = $1 '
18
+ * You may need another one, for example: {op: '~*}:
19
+ * ' AND <alias> ~* $1 '
20
+ * @param {*} options Optional:
21
+ * {
22
+ * fields: ['field1', 'field2']
23
+ * Only fields specified here will be taken from <filter>
24
+ * startWithWhere: If true (default), returned `where` will be 'WHERE ...'.
25
+ * If false, it will start like 'AND ...'
26
+ * }
27
+ *
28
+ * @returns
29
+ * [where, values]
30
+ *
31
+ *
32
+ * @example
33
+ * make_query_filter({'a': 10, 'b': [1,2,3], 'c': '11111', 'd': false}, {'d': {'alias': 'DEDO', 'coalesce': true}})
34
+ * [
35
+ * " WHERE a = $1 AND b IN ($2:csv) AND c = $3 AND COALESCE(DEDO, TRUE) IS FALSE ",
36
+ * [10, [1,2,3], '11111']
37
+ * ]
38
+ *
39
+ */
40
+ export const make_query_filter = (filter, modifiers = {}, options = {}) => {
41
+ let where = "",
42
+ values = []
43
+
44
+ if (filter) {
45
+ const startWithWhere = options?.startWithWhere !== false
46
+
47
+ let filter_fields = Object.keys(filter)
48
+ if (options?.fields) {
49
+ filter_fields = filter_fields.filter((f) => options.fields.indexOf(f) >= 0)
50
+ }
51
+
52
+ filter_fields.forEach((field_name) => {
53
+ const raw_value = filter[field_name]
54
+ if (raw_value === undefined || raw_value === null) {
55
+ return
56
+ }
57
+
58
+ const mod = modifiers[field_name] || {}
59
+
60
+ const prefix = values.length === 0 && startWithWhere ? " WHERE " : " AND "
61
+
62
+ const alias = mod?.alias || field_name
63
+ let filter_name = alias
64
+ if (mod?.coalesce) {
65
+ const def_value =
66
+ mod.coalesce === true ? "TRUE" : mod.coalesce === false ? "FALSE" : mod.coalesce
67
+ filter_name = `COALESCE(${alias}, ${def_value})`
68
+ }
69
+
70
+ const value = raw_value === "true" ? true : raw_value === "false" ? false : raw_value
71
+
72
+ let qfilter = ""
73
+
74
+ if (typeof value === "boolean") {
75
+ qfilter = `IS ${value === true ? "TRUE" : "FALSE"}`
76
+ } else {
77
+ if (Array.isArray(value)) {
78
+ qfilter = `IN ($${values.length + 1}:csv)`
79
+ values.push(value)
80
+ } else {
81
+ const op = mod?.op || "="
82
+ qfilter = `${op} $${values.length + 1}`
83
+ values.push(value)
84
+ }
85
+ }
86
+
87
+ where += ` ${prefix} ${filter_name} ${qfilter}`
88
+ })
89
+ }
90
+
91
+ return [where, values]
92
+ }
@@ -0,0 +1,29 @@
1
+ import Joi from "joi"
2
+ import { with_miolo_input_schema } from "miolo"
3
+ import { opt_int } from "#server/utils/schema.mjs"
4
+ import { db_todo_find } from "./find.mjs"
5
+
6
+ async function _db_todo_delete(ctx, params) {
7
+ ctx.miolo.logger.verbose(`[db_todo_delete] id: ${params?.id}`)
8
+
9
+ const conn = await ctx.miolo.db.get_connection()
10
+ // TODO : handle transactions
11
+ const options = { transaction: undefined }
12
+
13
+ const Todo = await conn.get_model("todo")
14
+
15
+ const todo = await db_todo_find(ctx, params.id)
16
+
17
+ if (todo == null) {
18
+ throw new Error(`[db_todo_delete] Trying to delete a todo that does not exist`)
19
+ }
20
+
21
+ await Todo.delete({ id: params.id }, options)
22
+ return params.id
23
+ }
24
+
25
+ const todo_delete_schema = Joi.object({
26
+ id: opt_int
27
+ })
28
+
29
+ export const db_todo_delete = with_miolo_input_schema(_db_todo_delete, todo_delete_schema)
@@ -0,0 +1,13 @@
1
+ import { db_todo_read } from "./read.mjs"
2
+
3
+ export async function db_todo_find(ctx, params) {
4
+ ctx.miolo.logger.verbose(`[db_todo_find] id: ${params?.filter?.id}`)
5
+
6
+ const todos = await db_todo_read(ctx, params)
7
+
8
+ if (todos.length === 0) {
9
+ return null
10
+ }
11
+
12
+ return todos[0]
13
+ }
@@ -0,0 +1,83 @@
1
+ import Joi from "joi"
2
+ import { with_miolo_input_schema } from "miolo"
3
+ import { make_query_filter } from "#server/io/db/filter.mjs"
4
+ import { bool_null, opt_int, opt_str_null } from "#server/utils/schema.mjs"
5
+
6
+ /**
7
+ * filter: {
8
+ * id / todo_id
9
+ * description
10
+ * done
11
+ * }
12
+ * options {
13
+ * limit : optional int
14
+ * }
15
+ */
16
+ async function _db_todo_read(ctx, { filter, options }) {
17
+ try {
18
+ ctx.miolo.logger.verbose(
19
+ `[db_todo_read] Reading todos with filter: ${JSON.stringify(filter)} and options ${JSON.stringify(options)}`
20
+ )
21
+
22
+ const conn = await ctx.miolo.db.get_connection()
23
+
24
+ let query = `
25
+ SELECT *
26
+ FROM todo AS t
27
+ *WHERE*`
28
+
29
+ if (options?.limit !== undefined) {
30
+ query += ` LIMIT ${options.limit}`
31
+ }
32
+
33
+ const [where, values] = make_query_filter(
34
+ filter,
35
+ {
36
+ todo_id: { alias: "t.id" },
37
+ description: { op: "~*" },
38
+ done: { coalesce: false, alias: "COALESCE(done, false)" }
39
+ },
40
+ {
41
+ fields: ["id", "todo_id", "description", "done"]
42
+ }
43
+ )
44
+
45
+ query = query.replace("*WHERE*", where)
46
+
47
+ // if (values.length == 0){
48
+ // ctx.miolo.logger.warn(`[prop_detail] Some filter must be specified`)
49
+ // return []
50
+ // }
51
+
52
+ const todos = await conn.select(query, values)
53
+
54
+ ctx.miolo.logger.verbose(`[db_todo_read] Read ${todos.length} todos`)
55
+
56
+ return todos
57
+ } catch (error) {
58
+ ctx.miolo.logger.error(`[db_todo_read] Error reading todos: ${error}`)
59
+ return []
60
+ }
61
+ }
62
+
63
+ /**
64
+ * filter: {
65
+ * id / todo_id
66
+ * description
67
+ * done
68
+ * }
69
+ */
70
+
71
+ const todo_read_schema = Joi.object({
72
+ filter: Joi.object({
73
+ id: opt_int,
74
+ todo_id: opt_int,
75
+ description: opt_str_null,
76
+ done: bool_null
77
+ }),
78
+ options: Joi.object({
79
+ limit: opt_int
80
+ })
81
+ })
82
+
83
+ export const db_todo_read = with_miolo_input_schema(_db_todo_read, todo_read_schema)
@@ -0,0 +1,37 @@
1
+ import Joi from "joi"
2
+ import { with_miolo_input_schema } from "miolo"
3
+ import { bool_null, opt_int } from "#server/utils/schema.mjs"
4
+ import { db_todo_find } from "./find.mjs"
5
+ import { db_todo_upsave } from "./upsave.mjs"
6
+
7
+ async function _db_todo_toggle(ctx, params) {
8
+ ctx.miolo.logger.verbose(`[db_todo_toggle] Toggling todo with tid ${params?.id}`)
9
+
10
+ const todo = await db_todo_find(ctx, params)
11
+
12
+ if (todo == null) {
13
+ throw new Error(`[db_todo_toggle] Trying to toggle a todo that does not exist`)
14
+ }
15
+
16
+ const done = !todo.done
17
+
18
+ const todo_data = {
19
+ id: parseInt(params.id, 10),
20
+ done
21
+ }
22
+
23
+ const nrecs = await db_todo_upsave(ctx, todo_data)
24
+
25
+ ctx.miolo.logger.verbose(
26
+ `[db_todo_toggle] Toggled todo with tid ${params?.id} (${nrecs} records updated)`
27
+ )
28
+
29
+ return done
30
+ }
31
+
32
+ const todo_toggle_schema = Joi.object({
33
+ id: opt_int,
34
+ done: bool_null
35
+ })
36
+
37
+ export const db_todo_toggle = with_miolo_input_schema(_db_todo_toggle, todo_toggle_schema)
@@ -0,0 +1,32 @@
1
+ import Joi from "joi"
2
+ import { with_miolo_input_schema } from "miolo"
3
+ import { bool_null, opt_int, opt_str_null } from "#server/utils/schema.mjs"
4
+
5
+ async function _db_todo_upsave(ctx, params) {
6
+ ctx.miolo.logger.verbose(`[db_todo_upsave] id: ${params?.id || "new"}`)
7
+
8
+ const conn = await ctx.miolo.db.get_connection()
9
+ // TODO : handle transactions
10
+ const options = { transaction: undefined }
11
+
12
+ const Todo = await conn.get_model("todo")
13
+
14
+ let tid = params?.id
15
+ if (params?.id !== undefined) {
16
+ const nrecs = await Todo.update(params, { id: params.id }, options)
17
+ ctx.miolo.logger.verbose(`[db_todo_upsave] Updated ${nrecs} todos`)
18
+ } else {
19
+ tid = await Todo.insert(params, options)
20
+ ctx.miolo.logger.verbose(`[db_todo_upsave] Inserted todo with id ${tid}`)
21
+ }
22
+
23
+ return { ...params, id: tid }
24
+ }
25
+
26
+ const todo_upsave_schema = Joi.object({
27
+ id: opt_int,
28
+ description: opt_str_null,
29
+ done: bool_null
30
+ })
31
+
32
+ export const db_todo_upsave = with_miolo_input_schema(_db_todo_upsave, todo_upsave_schema)
@@ -0,0 +1,13 @@
1
+ import { sha512 } from "#server/utils/crypt.mjs"
2
+
3
+ async function beforeInsertUser(_conn, params, options) {
4
+ const raw_pwd = params?.password
5
+ if (raw_pwd) {
6
+ const cry_pwd = sha512(raw_pwd, process.env.MIOLO_SESSION_SALT)
7
+ params.password = cry_pwd
8
+ }
9
+
10
+ return [params, options, true]
11
+ }
12
+
13
+ export { beforeInsertUser }