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