miolo 3.0.0-beta.203 → 3.0.0-beta.204

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "miolo",
3
- "version": "3.0.0-beta.203",
3
+ "version": "3.0.0-beta.204",
4
4
  "description": "all-in-one koa-based server",
5
5
  "author": "Donato Lorenzo <donato@afialapis.com>",
6
6
  "contributors": [
@@ -47,7 +47,7 @@
47
47
  "@babel/plugin-proposal-decorators": "^7.29.0",
48
48
  "@babel/preset-env": "^7.29.5",
49
49
  "@babel/preset-react": "^7.28.5",
50
- "@dotenvx/dotenvx": "^1.66.0",
50
+ "@dotenvx/dotenvx": "^1.67.0",
51
51
  "@koa/bodyparser": "^6.1.0",
52
52
  "@koa/cors": "^5.0.0",
53
53
  "@koa/router": "^15.5.0",
@@ -72,7 +72,7 @@
72
72
  "is-plain-object": "^5.0.0",
73
73
  "joi": "^18.2.1",
74
74
  "jwt-simple": "^0.5.6",
75
- "koa": "^3.2.0",
75
+ "koa": "^3.2.1",
76
76
  "koa-compress": "^5.2.1",
77
77
  "koa-connect": "^2.1.1",
78
78
  "koa-favicon": "^2.1.0",
@@ -83,7 +83,7 @@
83
83
  "koa-session": "^7.0.2",
84
84
  "koa-static": "^5.0.0",
85
85
  "nanoid": "^5.1.11",
86
- "nodemailer": "^8.0.7",
86
+ "nodemailer": "^8.0.8",
87
87
  "passport-google-oauth20": "^2.0.0",
88
88
  "passport-local": "^1.0.0",
89
89
  "rollup": "^4.60.4",
@@ -94,7 +94,7 @@
94
94
  "statuses": "^2.0.2",
95
95
  "tailwindcss": "^4.3.0",
96
96
  "tinguir": "^0.0.7",
97
- "vite": "^8.0.13",
97
+ "vite": "^8.0.14",
98
98
  "winston": "^3.19.0",
99
99
  "winston-daily-rotate-file": "^5.0.0",
100
100
  "yargs-parser": "^22.0.0"
@@ -476,10 +476,13 @@ export default function make_config_defaults() {
476
476
 
477
477
  socket: {
478
478
  enabled: false,
479
+ // Auto create rooms based on sessions
480
+ // To be able to do like: app.context.miolo.io.to('user_<id>').emit('ns', newData);
481
+ userRooms: true,
479
482
  cli: {
480
483
  /**
481
- domain: '',
482
- options: {}
484
+ url: '',
485
+ options: {}
483
486
  */
484
487
  }
485
488
  /*
@@ -1,4 +1,3 @@
1
- //import IO from 'koa-socket-2'
2
1
  import { Server } from "socket.io"
3
2
 
4
3
  function init_socket(app, config) {
@@ -12,8 +11,44 @@ function init_socket(app, config) {
12
11
 
13
12
  const io = new Server(app.http.server)
14
13
 
14
+ if (config?.userRooms === true) {
15
+ io.use(async (socket, next) => {
16
+ try {
17
+ const { store, options } = app.context.miolo.session || {}
18
+ if (store && options) {
19
+ // Create a fake Koa context to easily parse the signed cookies
20
+ const ctx = app.createContext(
21
+ socket.request,
22
+ socket.request.res || { headersSent: false }
23
+ )
24
+
25
+ // Read the session cookie (Koa handles signature validation if options.signed is true)
26
+ const sessionCookieValue = ctx.cookies.get(options.key, options)
27
+
28
+ if (sessionCookieValue) {
29
+ // If the cookie is present, koa-session stores the external key directly
30
+ // or sometimes it's base64 encoded. koa-session's decode function:
31
+ const sessionId = sessionCookieValue
32
+
33
+ const session = await store.get(sessionId, undefined, {})
34
+
35
+ if (session?.user) {
36
+ const userId = session.user?.id
37
+ socket.join(`user_${userId}`)
38
+ socket.mioloUser = userId // Attach for convenience
39
+ logger.info(`[socket] Socket ${socket.id} joined room user_${userId}`)
40
+ }
41
+ }
42
+ }
43
+ } catch (err) {
44
+ logger.error(`[socket] Error parsing session: ${err.message}`)
45
+ }
46
+ next()
47
+ })
48
+ }
49
+
15
50
  io.on("connection", (socket) => {
16
- logger.debug(`[socket] Connection from ... `) // ${i.ip} ${i.id}`)
51
+ logger.info(`[socket] Connection from ... `) // ${i.ip} ${i.id}`)
17
52
 
18
53
  if (config?.connection) {
19
54
  config.connection(socket)
@@ -24,42 +59,7 @@ function init_socket(app, config) {
24
59
  }
25
60
  })
26
61
 
27
- // const getInfo = (ctx) => {
28
- // let i= {id: '', ip: ''}
29
- // try {
30
- // i.id = ctx.socket.id
31
- // } catch (e) {}
32
- // try {
33
- // i.ip = ctx.socket.handshake.address
34
- // } catch (e) {}
35
- // return i
36
- // }
37
-
38
- // const io = new IO({ origins: '*:*'})
39
-
40
- // io.on('connection', function (ctx, data) {
41
- // const logger = ctx.miolo.logger
42
- // const i= getInfo(ctx)
43
- // logger.warn(`[socket] Connection from ${i.ip} ${i.id}`)
44
- // if (config?.connection) {
45
- // config.connection(ctx, data)
46
- // }
47
- // })
48
-
49
- // io.on('disconnect', function (ctx, data) {
50
- // const logger = ctx.miolo.logger
51
- // const i = getInfo(ctx)
52
- // logger.warn(`[socket] Disconnected ${i.ip} ${i.id} => ${data}`)
53
- // })
54
- //
55
- // io.on('error', function (ctx, data) {
56
- // const logger = ctx.miolo.logger
57
- // const i = getInfo(ctx)
58
- // logger.error(`[socket] Error on ${i.ip} ${i.id} => ${data}`)
59
- // })
60
- //
61
- //
62
- // io.attach(app)
62
+ app.context.miolo.io = io
63
63
  }
64
64
 
65
65
  export { init_socket }
@@ -18,6 +18,11 @@ function init_session_middleware(app, sessionConfig, cacheConfig) {
18
18
  ...(sessionConfig.options || {})
19
19
  }
20
20
 
21
+ app.context.miolo.session = {
22
+ store,
23
+ options
24
+ }
25
+
21
26
  app.use(createSession(options, app))
22
27
 
23
28
  app.use(async (ctx, next) => {
@@ -6,7 +6,7 @@ export async function init_ssr_render_middleware(app, config, devRender = undefi
6
6
  const ssrConfig = config.build.ssr
7
7
  const httpConfig = config.http
8
8
  const authConfig = config?.auth || {}
9
- // const socketConfig = config?.socket || {}
9
+ const socketConfig = config?.socket || {}
10
10
 
11
11
  const ssr_build_context = ssr_context_builder_make(app, ssrConfig)
12
12
  const ssr_loader = ssr_loader_make(app, ssrConfig)
@@ -32,11 +32,12 @@ export async function init_ssr_render_middleware(app, config, devRender = undefi
32
32
  logout_url:
33
33
  ctx.session?.auth_method === "google"
34
34
  ? authConfig?.passport?.google_url_logout
35
- : authConfig?.passport?.local_url_logout
36
- //socket: {
37
- // enabled: socketConfig?.enabled===true,
38
- // config: socketConfig?.config?.cli || {}
39
- //}
35
+ : authConfig?.passport?.local_url_logout,
36
+ socket: {
37
+ enabled: socketConfig?.enabled === true,
38
+ url: socketConfig?.config?.cli?.url || null,
39
+ options: socketConfig?.config?.cli?.options || {}
40
+ }
40
41
  }
41
42
  ctx.miolo.logger.debug(
42
43
  `[render-ssr] rendering an ${ctx?.session?.authenticated === true ? "authenticated" : "unauthenticated"} context...`
package/src/server.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import Koa from "koa"
2
2
  import { init_config } from "./config/index.mjs"
3
- //import {init_socket} from './engines/socket/index.mjs'
4
3
  import { init_cron } from "./engines/cron/index.mjs"
5
4
  import { init_http_server } from "./engines/http/index.mjs"
5
+ import { init_socket } from "./engines/socket/index.mjs"
6
6
  import { init_basic_auth_middleware } from "./middleware/auth/basic.mjs"
7
7
  import { init_guest_auth_middleware } from "./middleware/auth/guest.mjs"
8
8
  import { init_passport_auth_middleware } from "./middleware/auth/passport/index.mjs"
@@ -102,6 +102,9 @@ async function miolo(makeConfig, devInit = undefined, devRender = undefined) {
102
102
  }
103
103
 
104
104
  await app.http.start()
105
+ if (app.context.miolo?.io) {
106
+ app.context.miolo.io.attach(app.http.server)
107
+ }
105
108
  await app.cron.start()
106
109
  }
107
110
 
@@ -124,7 +127,7 @@ async function miolo(makeConfig, devInit = undefined, devRender = undefined) {
124
127
  }
125
128
 
126
129
  // Socket.io
127
- // init_socket(app, config?.socket)
130
+ init_socket(app, config?.socket)
128
131
 
129
132
  return app
130
133
  }
@@ -45,9 +45,9 @@
45
45
  "intre": "^3.0.0-beta.4",
46
46
  "joi": "^18.2.1",
47
47
  "lucide-react": "^1.16.0",
48
- "miolo-cli": "^3.0.0-beta.203",
48
+ "miolo-cli": "^3.0.0-beta.204",
49
49
  "miolo-model": "file:../miolo-model",
50
- "miolo-react": "^3.0.0-beta.203",
50
+ "miolo-react": "^3.0.0-beta.204",
51
51
  "next-themes": "^0.4.6",
52
52
  "radix-ui": "^1.4.3",
53
53
  "react": "^19.2.6",
@@ -62,8 +62,8 @@
62
62
  },
63
63
  "devDependencies": {
64
64
  "@biomejs/biome": "2.4.15",
65
- "miolo": "^3.0.0-beta.203",
66
- "sass-embedded": "^1.99.0"
65
+ "miolo": "^3.0.0-beta.204",
66
+ "sass-embedded": "^1.100.0"
67
67
  },
68
68
  "overrides": {
69
69
  "@babel/runtime": "^7.28.6",
@@ -1,4 +1,4 @@
1
- import { Bomb, Bone, CircleQuestionMark, RefreshCw } from "lucide-react"
1
+ import { Bomb, Bone, CircleQuestionMark, Phone, RefreshCw } from "lucide-react"
2
2
  import { useState } from "react"
3
3
  import { Field, FieldContent, FieldLabel } from "#cli/components/ui/field.jsx"
4
4
  import { Button } from "#cli/components/ui/patched/button.jsx"
@@ -12,8 +12,15 @@ const throwAnError = () => {
12
12
  }
13
13
 
14
14
  export default function TodoActions() {
15
- const { refreshTodoList, checkLastHours, insertFakeTodo, canEdit, useCrud, setUseCrud } =
16
- useTodosContext()
15
+ const {
16
+ refreshTodoList,
17
+ checkLastHours,
18
+ insertFakeTodo,
19
+ canEdit,
20
+ useCrud,
21
+ setUseCrud,
22
+ pingSocket
23
+ } = useTodosContext()
17
24
  const [hours, _setHours] = useState(1)
18
25
 
19
26
  return (
@@ -51,6 +58,15 @@ export default function TodoActions() {
51
58
  <Bomb size={18} />
52
59
  {`Throw an JS error`}
53
60
  </Button>
61
+
62
+ <Button
63
+ onClick={() => pingSocket()}
64
+ disabled={!canEdit}
65
+ className={`px-4 py-6 text-white rounded-lg transition-colors flex items-center gap-2 cursor-pointer ${useCrud ? "bg-pink-500 hover:bg-pink-400" : "bg-blue-500 hover:bg-blue-400"}`}
66
+ >
67
+ <Phone size={18} />
68
+ Ping through socket
69
+ </Button>
54
70
  </div>
55
71
 
56
72
  <h1 className="text-3xl font-bold text-center mt-8 mb-8 text-gray-800 dark:text-white">
@@ -1,4 +1,4 @@
1
- import { useCallback, useState } from "react"
1
+ import { useCallback, useEffect, useState } from "react"
2
2
  import useSessionContext from "#cli/context/session/useSessionContext.mjs"
3
3
  import useUIContext from "#cli/context/ui/useUIContext.mjs"
4
4
  import TodoList from "#ns/models/TodoList.mjs"
@@ -6,9 +6,10 @@ import TodosContext from "./TodosContext.jsx"
6
6
 
7
7
  const TodosProvider = ({ children }) => {
8
8
  // const [status, setStatus] = useState("loaded")
9
- const { useSsrData, fetcher, authenticated } = useSessionContext()
9
+ const { useSsrData, fetcher, socket, authenticated } = useSessionContext()
10
10
  const { toast } = useUIContext()
11
11
  const [useCrud, setUseCrud] = useState(true)
12
+ const [socketInited, setSocketInited] = useState(false)
12
13
 
13
14
  const {
14
15
  data: todoList,
@@ -132,6 +133,34 @@ const TodosProvider = ({ children }) => {
132
133
  refreshTodoList()
133
134
  }, [fetcher, refreshTodoList, toast])
134
135
 
136
+ const pingSocket = useCallback(() => {
137
+ if (!socket) {
138
+ toast.error("Socket not initialized")
139
+ return
140
+ }
141
+ socket.emit("ping", { timestamp: new Date().toISOString() })
142
+ }, [socket, toast])
143
+
144
+ useEffect(() => {
145
+ if (socket === undefined) {
146
+ return
147
+ }
148
+
149
+ if (socketInited) {
150
+ return
151
+ }
152
+ setSocketInited(true)
153
+
154
+ socket.on("connect", () => {
155
+ console.log("Connected to server!")
156
+ })
157
+
158
+ socket.on("todos-update", (data) => {
159
+ console.log("TODOS UPDATED!!!")
160
+ console.log(data)
161
+ })
162
+ }, [socket, socketInited])
163
+
135
164
  return (
136
165
  <TodosContext.Provider
137
166
  value={{
@@ -146,7 +175,8 @@ const TodosProvider = ({ children }) => {
146
175
  insertFakeTodo,
147
176
  canEdit: authenticated,
148
177
  useCrud,
149
- setUseCrud
178
+ setUseCrud,
179
+ pingSocket
150
180
  }}
151
181
  >
152
182
  {children}
@@ -19,6 +19,25 @@ export default () => {
19
19
  build: {
20
20
  ssr: { loader }
21
21
  },
22
- cron: init_cron()
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
+ }
23
42
  }
24
43
  }