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.
- package/bin/build/build.mjs +53 -0
- package/bin/build/build_bin.mjs +17 -0
- package/bin/build/cli/client.mjs +52 -0
- package/bin/build/cli/css.mjs +22 -0
- package/bin/build/cli/index.mjs +40 -0
- package/bin/build/cli/ssr.mjs +21 -0
- package/bin/build/server/aliases.mjs +112 -0
- package/bin/build/server/babel.config.js +24 -0
- package/bin/build/server/banner.mjs +20 -0
- package/bin/build/server/bundle.mjs +111 -0
- package/bin/build/server/fix.mjs +15 -0
- package/bin/build/server/index.mjs +69 -0
- package/bin/build/server/options.mjs +83 -0
- package/bin/create/auth.mjs +23 -0
- package/bin/create/copy.mjs +175 -0
- package/bin/create/docker.mjs +25 -0
- package/bin/create/index.mjs +137 -0
- package/bin/create/pkgjson.mjs +72 -0
- package/bin/create/prepare-template.mjs +158 -0
- package/bin/create/validation.mjs +27 -0
- package/bin/dev/dev.mjs +32 -23
- package/bin/dev/dev_start.mjs +6 -5
- package/bin/index.mjs +94 -52
- package/bin/prod-bin/create-bin.mjs +42 -27
- package/bin/prod-bin/run.mjs +13 -9
- package/bin/{prod-run → run}/pid.mjs +4 -4
- package/bin/run/restart.mjs +13 -0
- package/bin/run/start.mjs +18 -0
- package/bin/run/stop.mjs +20 -0
- package/bin/util.mjs +35 -11
- package/package.json +59 -39
- package/src/config/.env +34 -12
- package/src/config/defaults.mjs +253 -185
- package/src/config/env.mjs +40 -22
- package/src/config/index.mjs +19 -24
- package/src/config/util.mjs +25 -10
- package/src/db-conn.mjs +34 -0
- package/src/engines/cron/emails.mjs +10 -5
- package/src/engines/cron/index.mjs +45 -51
- package/src/engines/cron/init.mjs +16 -17
- package/src/engines/cron/ipsum.mjs +65 -60
- package/src/engines/cron/syscheck.mjs +30 -30
- package/src/engines/emailer/index.mjs +1 -2
- package/src/engines/emailer/queue.mjs +14 -20
- package/src/engines/emailer/transporter.mjs +86 -74
- package/src/engines/geoip/index.mjs +23 -28
- package/src/engines/http/index.mjs +26 -15
- package/src/engines/logger/buildErrorEmailBody.mjs +72 -0
- package/src/engines/logger/index.mjs +114 -122
- package/src/engines/logger/injectStackTrace.mjs +59 -0
- package/src/engines/logger/logger_mail.mjs +47 -61
- package/src/engines/logger/reopenTransportOnHupSignal.mjs +12 -13
- package/src/engines/parser/Parser.mjs +77 -60
- package/src/engines/parser/index.mjs +1 -1
- package/src/engines/schema/diffObjs.mjs +41 -0
- package/src/engines/schema/index.mjs +4 -0
- package/src/engines/schema/input.mjs +54 -0
- package/src/engines/schema/output.mjs +66 -0
- package/src/engines/socket/index.mjs +44 -46
- package/src/index.mjs +15 -10
- package/src/middleware/auth/basic.mjs +41 -40
- package/src/middleware/auth/custom.mjs +10 -13
- package/src/middleware/auth/guest.mjs +27 -27
- package/src/middleware/auth/passport/index.mjs +374 -0
- package/src/middleware/auth/passport/session/index.mjs +43 -0
- package/src/middleware/auth/{credentials → passport}/session/store.mjs +35 -15
- package/src/middleware/auth/passport/session/store_koa_redis.mjs +3 -0
- package/src/middleware/context/cache/index.mjs +78 -33
- package/src/middleware/context/cache/options.mjs +19 -21
- package/src/middleware/context/db.mjs +45 -20
- package/src/middleware/context/index.mjs +12 -12
- package/src/middleware/extra.mjs +4 -5
- package/src/middleware/http/body.mjs +25 -25
- package/src/middleware/http/catcher.mjs +81 -8
- package/src/middleware/http/custom_blacklist.mjs +19 -16
- package/src/middleware/http/headers.mjs +37 -34
- package/src/middleware/http/ratelimit.mjs +16 -23
- package/src/middleware/http/request.mjs +60 -65
- package/src/middleware/routes/catch_js_error.mjs +30 -23
- package/src/middleware/routes/robots.mjs +4 -7
- package/src/middleware/routes/router/crud/attachCrudRoutes.mjs +108 -90
- package/src/middleware/routes/router/crud/getCrudConfig.mjs +31 -55
- package/src/middleware/routes/router/defaults.mjs +6 -19
- package/src/middleware/routes/router/index.mjs +17 -21
- package/src/middleware/routes/router/queries/attachQueriesRoutes.mjs +227 -50
- package/src/middleware/routes/router/queries/getQueriesConfig.mjs +45 -55
- package/src/middleware/routes/router/utils.mjs +41 -26
- package/src/middleware/ssr/context.mjs +5 -7
- package/src/middleware/ssr/html.mjs +66 -43
- package/src/middleware/ssr/loader.mjs +11 -14
- package/src/middleware/ssr/ssr_render.mjs +39 -22
- package/src/middleware/static/index.mjs +33 -14
- package/src/middleware/vite/devserver.mjs +38 -22
- package/src/middleware/vite/watcher.mjs +12 -14
- package/src/server-cron.mjs +13 -8
- package/src/server-dev.mjs +13 -16
- package/src/server.mjs +49 -51
- package/template/.agent/skills/miolo-app-arch/SKILL.md +218 -0
- package/template/.agent/skills/miolo-auth/SKILL.md +450 -0
- package/template/.agent/skills/miolo-cli-router/SKILL.md +394 -0
- package/template/.agent/skills/miolo-database/SKILL.md +358 -0
- package/template/.agent/skills/miolo-react-patterns/SKILL.md +426 -0
- package/template/.agent/skills/miolo-routing/SKILL.md +326 -0
- package/template/.agent/skills/miolo-schemas/SKILL.md +329 -0
- package/template/.agent/skills/miolo-session-context/SKILL.md +397 -0
- package/template/.agent/skills/miolo-ssr/SKILL.md +433 -0
- package/template/.editorconfig +18 -0
- package/template/.env +120 -0
- package/template/biome.json +63 -0
- package/template/components.json +21 -0
- package/template/db/init.sh +89 -0
- package/template/db/sql/00_drop.sql +2 -0
- package/template/db/sql/01_users.sql +31 -0
- package/template/db/sql/02_todos.sql +20 -0
- package/template/docker/Dockerfile +13 -0
- package/template/docker/docker-compose.yaml +79 -0
- package/template/gitignore +42 -0
- package/template/jsconfig.json +18 -0
- package/template/package.json +88 -0
- package/template/postcss.config.js +9 -0
- package/template/src/cli/App.jsx +25 -0
- package/template/src/cli/components/JsonTreeViewer.jsx +128 -0
- package/template/src/cli/components/shadcn-io/spinner/index.jsx +232 -0
- package/template/src/cli/components/stepper.jsx +408 -0
- package/template/src/cli/components/ui/avatar.jsx +36 -0
- package/template/src/cli/components/ui/badge.jsx +31 -0
- package/template/src/cli/components/ui/breadcrumb.jsx +97 -0
- package/template/src/cli/components/ui/card.jsx +73 -0
- package/template/src/cli/components/ui/collapsible.jsx +16 -0
- package/template/src/cli/components/ui/dropdown-menu.jsx +179 -0
- package/template/src/cli/components/ui/field.jsx +217 -0
- package/template/src/cli/components/ui/input.jsx +19 -0
- package/template/src/cli/components/ui/label.jsx +17 -0
- package/template/src/cli/components/ui/pagination.jsx +99 -0
- package/template/src/cli/components/ui/patched/alert.jsx +56 -0
- package/template/src/cli/components/ui/patched/button.jsx +45 -0
- package/template/src/cli/components/ui/patched/dialog.jsx +114 -0
- package/template/src/cli/components/ui/patched/sidebar.jsx +660 -0
- package/template/src/cli/components/ui/select.jsx +141 -0
- package/template/src/cli/components/ui/separator.jsx +21 -0
- package/template/src/cli/components/ui/sheet.jsx +115 -0
- package/template/src/cli/components/ui/skeleton.jsx +13 -0
- package/template/src/cli/components/ui/sonner.jsx +22 -0
- package/template/src/cli/components/ui/switch.jsx +25 -0
- package/template/src/cli/components/ui/table.jsx +88 -0
- package/template/src/cli/components/ui/textarea.jsx +16 -0
- package/template/src/cli/components/ui/tooltip.jsx +45 -0
- package/template/src/cli/config/store_keys.mjs +2 -0
- package/template/src/cli/context/data/DataContext.jsx +5 -0
- package/template/src/cli/context/data/DataProvider.jsx +44 -0
- package/template/src/cli/context/data/useBreads.mjs +15 -0
- package/template/src/cli/context/data/useDataContext.mjs +4 -0
- package/template/src/cli/context/session/SessionContext.mjs +4 -0
- package/template/src/cli/context/session/SessionProvider.jsx +31 -0
- package/template/src/cli/context/session/makePermissioner.mjs +34 -0
- package/template/src/cli/context/session/useSessionContext.mjs +6 -0
- package/template/src/cli/context/theme/ThemeContext.mjs +4 -0
- package/template/src/cli/context/theme/ThemeProvider.jsx +49 -0
- package/template/src/cli/context/theme/useThemeContext.mjs +6 -0
- package/template/src/cli/context/ui/UIContext.jsx +5 -0
- package/template/src/cli/context/ui/UIProvider.jsx +16 -0
- package/template/src/cli/context/ui/useUIContext.mjs +4 -0
- package/template/src/cli/context/util.mjs +17 -0
- package/template/src/cli/entry-cli.jsx +33 -0
- package/template/src/cli/hooks/useIsMobile.mjs +19 -0
- package/template/src/cli/hooks/useStoragedState.mjs +63 -0
- package/template/src/cli/index.html +29 -0
- package/template/src/cli/layout/app-sidebar.jsx +25 -0
- package/template/src/cli/layout/main-layout.jsx +63 -0
- package/template/src/cli/layout/nav-last-todos.jsx +72 -0
- package/template/src/cli/layout/nav-main.jsx +39 -0
- package/template/src/cli/layout/nav-user.jsx +105 -0
- package/template/src/cli/layout/prop-switcher.jsx +93 -0
- package/template/src/cli/lib/utils.mjs +10 -0
- package/template/src/cli/pages/Index.jsx +13 -0
- package/template/src/cli/pages/IndexOffline.jsx +13 -0
- package/template/src/cli/pages/IndexOnline.jsx +18 -0
- package/template/src/cli/pages/dash/Dashboard.jsx +29 -0
- package/template/src/cli/pages/offline/Login.jsx +43 -0
- package/template/src/cli/pages/offline/LoginForm.jsx +115 -0
- package/template/src/cli/pages/security/Security.jsx +39 -0
- package/template/src/cli/pages/security/SecurityForm.jsx +106 -0
- package/template/src/cli/pages/todos/TodoActions.jsx +99 -0
- package/template/src/cli/pages/todos/TodoAdd.jsx +43 -0
- package/template/src/cli/pages/todos/TodoList.jsx +60 -0
- package/template/src/cli/pages/todos/Todos.jsx +23 -0
- package/template/src/cli/pages/todos/context/TodosContext.jsx +5 -0
- package/template/src/cli/pages/todos/context/TodosProvider.jsx +191 -0
- package/template/src/cli/pages/todos/context/useTodosContext.mjs +4 -0
- package/template/src/ns/models/Todo.mjs +29 -0
- package/template/src/ns/models/TodoList.mjs +8 -0
- package/template/src/ns/models/User.mjs +40 -0
- package/template/src/server/bot/check_today.mjs +10 -0
- package/template/src/server/io/cache/base.mjs +21 -0
- package/template/src/server/io/db/filter.mjs +92 -0
- package/template/src/server/io/db/todos/delete.mjs +29 -0
- package/template/src/server/io/db/todos/find.mjs +13 -0
- package/template/src/server/io/db/todos/read.mjs +83 -0
- package/template/src/server/io/db/todos/toggle.mjs +37 -0
- package/template/src/server/io/db/todos/upsave.mjs +32 -0
- package/template/src/server/io/db/triggers/user.mjs +13 -0
- package/template/src/server/io/db/users/auth.mjs +132 -0
- package/template/src/server/io/db/users/pwd.mjs +38 -0
- package/template/src/server/io/db/users/save.mjs +17 -0
- package/template/src/server/miolo/auth/basic.mjs +15 -0
- package/template/src/server/miolo/auth/guest.mjs +3 -0
- package/template/src/server/miolo/auth/passport.mjs +73 -0
- package/template/src/server/miolo/cache.mjs +11 -0
- package/template/src/server/miolo/cron/foo.mjs +7 -0
- package/template/src/server/miolo/cron/index.mjs +28 -0
- package/template/src/server/miolo/cron/invalidate.mjs +21 -0
- package/template/src/server/miolo/db.mjs +36 -0
- package/template/src/server/miolo/http.mjs +14 -0
- package/template/src/server/miolo/index.mjs +43 -0
- package/template/src/server/miolo/routes/crud.mjs +16 -0
- package/template/src/server/miolo/routes/index.mjs +8 -0
- package/template/src/server/miolo/ssr/entry-server.jsx +13 -0
- package/template/src/server/miolo/ssr/loader.mjs +18 -0
- package/template/src/server/routes/index.mjs +66 -0
- package/template/src/server/routes/todos/mod.mjs +52 -0
- package/template/src/server/routes/todos/read.mjs +45 -0
- package/template/src/server/routes/todos/special.mjs +47 -0
- package/template/src/server/routes/users/user.mjs +54 -0
- package/template/src/server/server.mjs +10 -0
- package/template/src/server/utils/crypt.mjs +38 -0
- package/template/src/server/utils/io.mjs +15 -0
- package/template/src/server/utils/pwdfor.mjs +25 -0
- package/template/src/server/utils/schema.mjs +22 -0
- package/template/src/static/img/default/profile.png +0 -0
- package/template/src/static/img/favicon.ico +0 -0
- package/template/src/static/img/miolo_logo.png +0 -0
- package/template/src/static/img/miolo_name.png +0 -0
- package/template/src/static/public/manifest.json +21 -0
- package/template/src/static/public/sw.js +79 -0
- package/template/src/static/style/globals.css +156 -0
- package/template/src/static/style/json-tree.css +54 -0
- package/template/src/static/style/skeleton.css +49 -0
- package/bin/prod-build/build-client.mjs +0 -67
- package/bin/prod-build/build-server.mjs +0 -58
- package/bin/prod-run/restart.mjs +0 -9
- package/bin/prod-run/start.mjs +0 -15
- package/bin/prod-run/stop.mjs +0 -20
- package/src/engines/logger/verify.mjs +0 -22
- package/src/middleware/auth/credentials/index.mjs +0 -151
- package/src/middleware/auth/credentials/session/index.mjs +0 -24
- 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,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,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,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 }
|