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