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