@raclettejs/core 0.1.35 → 0.1.36-canary.0
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/CHANGELOG.md +24 -1
- package/dist/cli.js +86 -82
- package/dist/cli.js.map +3 -3
- package/package.json +5 -5
- package/services/.dockerignore +2 -0
- package/services/backend/.dockerignore +3 -1
- package/services/backend/src/core/payload/payloadTypes.ts +1 -0
- package/services/backend/src/core/pluginSystem/configGenerator/index.ts +12 -11
- package/services/backend/src/core/pluginSystem/pluginFastifyInstance.ts +28 -3
- package/services/backend/src/core/pluginSystem/pluginLoader.ts +11 -2
- package/services/backend/src/core/pluginSystem/pluginTypes.ts +39 -1
- package/services/backend/src/core/sockets/createSystemEventHandlers.ts +4 -4
- package/services/backend/src/core/sockets/socketManager.ts +80 -32
- package/services/backend/src/core/sockets/socketTypes.ts +6 -0
- package/services/backend/src/core/validation/schemaGenerator.ts +10 -3
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/project.service.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/index.ts +0 -2
- package/services/backend/src/modules/authentication/index.ts +15 -23
- package/services/backend/src/shared/helper/adminTag.ts +34 -0
- package/services/backend/src/shared/types/plugins/index.ts +1 -9
- package/services/backend/src/utils/request.utils.ts +1 -1
- package/services/frontend/.dockerignore +3 -1
- package/services/frontend/src/core/lib/data/dataApi.ts +27 -16
- package/services/frontend/src/core/lib/data/queryApi.ts +17 -8
- package/services/frontend/src/core/lib/data/responseTypeHandler.ts +17 -8
- package/services/frontend/src/core/lib/data/writeDataHandler.ts +3 -1
- package/services/frontend/src/core/lib/dataHelper.ts +51 -12
- package/services/frontend/src/core/lib/eventWhitelist.ts +2 -0
- package/services/frontend/src/core/lib/httpClient.ts +16 -2
- package/services/frontend/src/core/lib/userNotifier.ts +20 -0
- package/services/frontend/src/core/lib/workSessionUrl.ts +16 -0
- package/services/frontend/src/core/main.ts +82 -15
- package/services/frontend/src/core/setup/socketBootstrap.ts +83 -0
- package/services/frontend/src/core/setup/socketClient.ts +39 -39
- package/services/frontend/src/core/setup/socketEvents.ts +152 -158
- package/services/frontend/src/core/setup/socketRegistry.ts +19 -0
- package/services/frontend/src/core/types/DataApi.ts +10 -2
- package/services/frontend/src/core/types/PluginRegistrar.ts +1 -1
- package/services/frontend/src/orchestrator/ProductOrchestrator.vue +14 -0
- package/services/frontend/src/orchestrator/assets/styles/layers.css +4 -1
- package/services/frontend/src/orchestrator/components/dataTable/BaseDataTable.vue +267 -35
- package/services/frontend/src/orchestrator/components/menu/ServerStatus.vue +11 -2
- package/services/frontend/src/orchestrator/composables/useVueQueryObservableHelper.ts +12 -7
- package/services/frontend/src/orchestrator/composables/useVueWriteOperationHelper.ts +12 -6
- package/services/frontend/src/orchestrator/i18n/de-DE.json +1 -0
- package/services/frontend/src/orchestrator/i18n/en-EU.json +1 -0
- package/services/frontend/src/orchestrator/i18n/sk.json +1 -0
- package/services/frontend/src/orchestrator/i18n/tl-TL.json +1 -0
- package/services/frontend/src/orchestrator/index.ts +7 -24
- package/services/frontend/src/orchestrator/lib/queryParamsSource.ts +48 -0
- package/services/frontend/src/orchestrator/main.ts +2 -2
- package/services/frontend/src/orchestrator/router/index.ts +0 -19
- package/services/frontend/src/orchestrator/router/registerEventHandlers.ts +29 -0
- package/services/frontend/src/orchestrator/router/routeStore.ts +92 -17
- package/services/frontend/src/orchestrator/router/routes.ts +5 -0
- package/services/frontend/src/orchestrator/setup/application.ts +11 -2
- package/services/frontend/src/orchestrator/types/DataApi.ts +19 -0
- package/services/frontend/src/orchestrator/types/PluginApi.ts +4 -7
- package/services/frontend/src/orchestrator/types/Widgets.ts +2 -0
- package/services/frontend/src/orchestrator/types/index.ts +1 -0
- package/templates/fs.yml +1 -1
- package/package/dist/index.js +0 -452
- package/services/backend/.yarn/install-state.gz +0 -0
- package/services/backend/dist/core/eventBus/index.js +0 -7
- package/services/backend/dist/core/pluginSystem/configGenerator/index.js +0 -346
- package/services/backend/dist/core/sockets/index.js +0 -95
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/account/events/index.js +0 -31
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/account/index.js +0 -48
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/account/routes/index.js +0 -15
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/composition/events/index.js +0 -22
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/composition/index.js +0 -51
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/composition/routes/index.js +0 -19
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/interactionLink/events/index.js +0 -22
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/interactionLink/index.js +0 -48
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/index.js +0 -19
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/project/events/index.js +0 -31
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/project/index.js +0 -49
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/project/routes/index.js +0 -16
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/tag/events/index.js +0 -39
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/tag/index.js +0 -50
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/tag/routes/index.js +0 -19
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/user/events/index.js +0 -31
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/user/index.js +0 -51
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/user/routes/index.js +0 -21
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/workSession/events/index.js +0 -31
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/workSession/index.js +0 -48
- package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/workSession/routes/index.js +0 -15
- package/services/backend/dist/corePlugins/raclette__core/backend/index.js +0 -34
- package/services/backend/dist/domains/index.js +0 -11
- package/services/backend/dist/domains/system/index.js +0 -11
- package/services/backend/dist/domains/system/routes/index.js +0 -17
- package/services/backend/dist/helpers/index.js +0 -14
- package/services/backend/dist/index.js +0 -3
- package/services/backend/dist/modules/authentication/index.js +0 -253
- package/services/backend/dist/shared/types/core/index.js +0 -8
- package/services/backend/dist/shared/types/dataTypes/index.js +0 -6
- package/services/backend/dist/shared/types/index.js +0 -8
- package/services/backend/dist/shared/types/plugins/index.js +0 -8
- package/services/backend/dist/types/index.js +0 -12
- package/services/backend/dist/utils/index.js +0 -2
- package/services/frontend/.yarn/install-state.gz +0 -0
|
@@ -3,59 +3,59 @@
|
|
|
3
3
|
* Listens to the event bus and emits accordingly messages to the socket server.
|
|
4
4
|
*/
|
|
5
5
|
import log from "@racletteCore/lib/logger"
|
|
6
|
+
import { $store } from "@racletteCore"
|
|
6
7
|
import { getUserToken } from "./sessionHandler"
|
|
7
8
|
import configService from "@racletteCore/lib/configService"
|
|
8
|
-
import {
|
|
9
|
+
import { extractSessionIdFromUrl } from "@racletteCore/lib/workSessionUrl"
|
|
10
|
+
import { getActiveSocket, setActiveSocket } from "./socketRegistry"
|
|
11
|
+
import type { Socket } from "socket.io-client"
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
* Extract sessionId from URL if present
|
|
12
|
-
* Supports racletteJS route format: /:workSession/id={uuid}
|
|
13
|
-
*/
|
|
14
|
-
const extractSessionIdFromUrl = (): string | null => {
|
|
15
|
-
const path = window.location.pathname + (window.location.hash || "")
|
|
16
|
-
// Match /:workSession/id={uuid} pattern
|
|
17
|
-
const workSessionMatch = path.match(/\/:workSession\/id=([^/&#]+)/)
|
|
18
|
-
|
|
19
|
-
if (workSessionMatch && workSessionMatch[1]) {
|
|
20
|
-
const sessionId = workSessionMatch[1]
|
|
21
|
-
// Validate UUID format
|
|
22
|
-
if (validate(sessionId)) {
|
|
23
|
-
return sessionId
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return null
|
|
28
|
-
}
|
|
13
|
+
let eventbusRegistered = false
|
|
29
14
|
|
|
30
|
-
const registerSocketClient = ($socket, $eventbus) => {
|
|
31
|
-
$
|
|
32
|
-
const { requireAuthentication } = configService.getConfig()?.global ?? {
|
|
33
|
-
requireAuthentication: true,
|
|
34
|
-
}
|
|
15
|
+
const registerSocketClient = ($socket: Socket, $eventbus) => {
|
|
16
|
+
setActiveSocket($socket)
|
|
35
17
|
|
|
36
|
-
|
|
37
|
-
|
|
18
|
+
if (eventbusRegistered) {
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
eventbusRegistered = true
|
|
38
22
|
|
|
39
|
-
|
|
40
|
-
|
|
23
|
+
$eventbus.on(
|
|
24
|
+
"socket/join/room",
|
|
25
|
+
(roomId?: string, options?: { bootstrap?: boolean }) => {
|
|
26
|
+
const { requireAuthentication } = configService.getConfig()?.global ?? {
|
|
27
|
+
requireAuthentication: true,
|
|
28
|
+
}
|
|
41
29
|
|
|
42
|
-
|
|
43
|
-
|
|
30
|
+
const sessionId = extractSessionIdFromUrl()
|
|
31
|
+
const isWorkSession = !!sessionId
|
|
44
32
|
|
|
45
|
-
|
|
46
|
-
// The sessionId was already sent during socket handshake in the auth object
|
|
47
|
-
if (!token && !isWorkSession) {
|
|
33
|
+
if (requireAuthentication && !isWorkSession && !getUserToken()) {
|
|
48
34
|
return log.socket("emitJoin", "no token and not a work session")
|
|
49
35
|
}
|
|
50
|
-
}
|
|
51
36
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
37
|
+
// Light re-join: after a transport reconnect the project is still in store;
|
|
38
|
+
// skip full bootstrap and let the server use readMain (no roomId).
|
|
39
|
+
const projectId = $store.getState(["project", "_id"]) as string | null
|
|
40
|
+
const isLightRejoin =
|
|
41
|
+
options?.bootstrap === false ||
|
|
42
|
+
(!roomId && !!projectId && options?.bootstrap !== true)
|
|
43
|
+
const bootstrap =
|
|
44
|
+
options?.bootstrap ?? (isLightRejoin ? false : !roomId)
|
|
45
|
+
|
|
46
|
+
log.socket("emitJoin", {
|
|
47
|
+
roomId,
|
|
48
|
+
bootstrap,
|
|
49
|
+
isWorkSession,
|
|
50
|
+
lightRejoin: isLightRejoin,
|
|
51
|
+
})
|
|
52
|
+
getActiveSocket()?.emit("join", { roomId, bootstrap })
|
|
53
|
+
},
|
|
54
|
+
)
|
|
55
55
|
|
|
56
56
|
$eventbus.on("socket/leave/room", (roomId) => {
|
|
57
57
|
log.socket("emitLeave", { roomId })
|
|
58
|
-
|
|
58
|
+
getActiveSocket()?.emit("leave", roomId)
|
|
59
59
|
})
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -1,91 +1,85 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* listen to socket events received from the backend
|
|
3
|
-
*
|
|
4
|
-
* @param {object} $socket - a socketClient instance
|
|
5
|
-
* @param {object} $store - a minirx-store instance, so we can dispatch actions
|
|
6
|
-
* @param {object} $eventbus - instance of our custom eventbus
|
|
7
|
-
*/
|
|
8
1
|
import type { Composition } from "@shared/types/core/Composition.types"
|
|
9
2
|
import type { InteractionLink } from "@shared/types/core/InteractionLink.types"
|
|
10
3
|
import log from "@racletteCore/lib/logger"
|
|
11
|
-
import { setupSpace } from "@racletteCore/lib/projectDataHelper"
|
|
12
4
|
import { handleServerItem } from "@racletteCore/lib/data/dataApi"
|
|
13
5
|
import * as userNotifier from "@racletteCore/lib/userNotifier"
|
|
14
6
|
import type { ProjectConfiguration } from "../store/types"
|
|
15
|
-
import {
|
|
7
|
+
import type { Socket } from "socket.io-client"
|
|
8
|
+
import { extractSessionIdFromUrl } from "@racletteCore/lib/workSessionUrl"
|
|
16
9
|
import { setHttpToken, setWorkSessionId } from "@racletteCore/lib/httpClient"
|
|
10
|
+
import { isActiveSocket, setActiveSocket } from "./socketRegistry"
|
|
11
|
+
import { startProjectBootstrap } from "./socketBootstrap"
|
|
17
12
|
|
|
18
13
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
14
|
+
* Listen to socket events received from the backend.
|
|
15
|
+
*
|
|
16
|
+
* @param $socket - socket.io client instance
|
|
17
|
+
* @param $store - mini-rx store (dispatch actions)
|
|
18
|
+
* @param $eventbus - global event bus
|
|
19
|
+
*
|
|
20
|
+
* Project bootstrap after joinConfirmation is handled in ./socketBootstrap.ts.
|
|
21
21
|
*/
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
// Match /:workSession/id={uuid} pattern
|
|
25
|
-
const workSessionMatch = path.match(/\/:workSession\/id=([^/&#]+)/)
|
|
26
|
-
|
|
27
|
-
if (workSessionMatch && workSessionMatch[1]) {
|
|
28
|
-
const sessionId = workSessionMatch[1]
|
|
29
|
-
// Validate UUID format
|
|
30
|
-
if (validate(sessionId)) {
|
|
31
|
-
return sessionId
|
|
32
|
-
}
|
|
33
|
-
}
|
|
22
|
+
const registerSocketEvents = ($socket: Socket, $store, $eventbus) => {
|
|
23
|
+
setActiveSocket($socket)
|
|
34
24
|
|
|
35
|
-
|
|
36
|
-
|
|
25
|
+
/** Ignore events from a replaced socket instance (see socketRegistry + main.ts teardown). */
|
|
26
|
+
const guardActive = (socket: Socket) => isActiveSocket(socket)
|
|
37
27
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
28
|
+
// --- Connection lifecycle ---------------------------------------------------
|
|
29
|
+
|
|
30
|
+
/** Fired when a connection is established; triggers room join. */
|
|
31
|
+
$socket.on("connect", () => {
|
|
32
|
+
if (!guardActive($socket)) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
44
35
|
log.socket("onConnect", "Socket connection established")
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
payload: { status: 1 },
|
|
48
|
-
})
|
|
49
|
-
// @TODO if we re-connect after a drop out we need to subscribe to our project without
|
|
50
|
-
// completely setting up the project up, e.g. only re-join project room.
|
|
36
|
+
userNotifier.setServerConnected()
|
|
37
|
+
$eventbus.emit("socket/join/room")
|
|
51
38
|
})
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
39
|
+
|
|
40
|
+
/** Enable offline mode here if project config disabled it. */
|
|
41
|
+
$socket.on("disconnect", (reason) => {
|
|
42
|
+
if (!guardActive($socket)) {
|
|
43
|
+
log.socket("onDisconnect", `ignored stale socket instance: ${reason}`)
|
|
44
|
+
return
|
|
45
|
+
}
|
|
56
46
|
log.socket(
|
|
57
47
|
"onDisconnect",
|
|
58
|
-
|
|
48
|
+
`Socket disconnected (${reason}). Enable offline mode here if project conf disabled it`,
|
|
59
49
|
)
|
|
60
50
|
userNotifier.setServerDisconnected()
|
|
61
51
|
})
|
|
52
|
+
|
|
62
53
|
$socket.on("connect_error", (err) => {
|
|
54
|
+
if (!guardActive($socket)) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
63
57
|
log.socket("onConnectError", err)
|
|
64
58
|
userNotifier.setServerDisconnected()
|
|
65
59
|
})
|
|
66
|
-
|
|
67
|
-
* close
|
|
68
|
-
*/
|
|
60
|
+
|
|
69
61
|
$socket.on("close", () => {
|
|
62
|
+
if (!guardActive($socket)) {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
70
65
|
log.socket("onClose", "socket connection lost")
|
|
71
66
|
userNotifier.setServerDisconnected()
|
|
72
67
|
})
|
|
68
|
+
|
|
73
69
|
/**
|
|
74
|
-
*
|
|
70
|
+
* Reconnect attempt in progress (status 2).
|
|
71
|
+
* connect handler will set online status and re-join the room.
|
|
75
72
|
*/
|
|
76
|
-
$socket.on("reconnect", () => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
})
|
|
73
|
+
$socket.on("reconnect", (attempt) => {
|
|
74
|
+
if (!guardActive($socket)) {
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
log.socket("onReconnect", `Socket reconnect (attempt ${attempt})`)
|
|
78
|
+
userNotifier.setServerConnecting()
|
|
83
79
|
})
|
|
84
80
|
|
|
85
|
-
/**
|
|
86
|
-
*
|
|
87
|
-
* const data = { id: 'you'session'id' }
|
|
88
|
-
*/
|
|
81
|
+
/** Server assigned this socket an id
|
|
82
|
+
* payload: { id: string } */
|
|
89
83
|
$socket.on("socketEstablished", (data) => {
|
|
90
84
|
log.socket("onSocketEstablished", data)
|
|
91
85
|
$store.dispatch({
|
|
@@ -94,9 +88,8 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
|
|
|
94
88
|
})
|
|
95
89
|
})
|
|
96
90
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
*/
|
|
91
|
+
// --- Entity sync (server → store / event bus) ------------------------------
|
|
92
|
+
|
|
100
93
|
$socket.on("tagDeleted", (payload) => {
|
|
101
94
|
const { appSessionId, body, type } = payload
|
|
102
95
|
|
|
@@ -112,9 +105,7 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
|
|
|
112
105
|
body,
|
|
113
106
|
})
|
|
114
107
|
})
|
|
115
|
-
|
|
116
|
-
* tagCreated
|
|
117
|
-
*/
|
|
108
|
+
|
|
118
109
|
$socket.on("tagCreated", (payload) => {
|
|
119
110
|
const { appSessionId, body, type } = payload
|
|
120
111
|
|
|
@@ -130,9 +121,7 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
|
|
|
130
121
|
body,
|
|
131
122
|
})
|
|
132
123
|
})
|
|
133
|
-
|
|
134
|
-
* userCreated
|
|
135
|
-
*/
|
|
124
|
+
|
|
136
125
|
$socket.on("userCreated", (payload) => {
|
|
137
126
|
const { appSessionId, body, type } = payload
|
|
138
127
|
|
|
@@ -143,9 +132,7 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
|
|
|
143
132
|
})
|
|
144
133
|
$eventbus.emit("socket_createdUser", { offline: false, body })
|
|
145
134
|
})
|
|
146
|
-
|
|
147
|
-
* userDeleted
|
|
148
|
-
*/
|
|
135
|
+
|
|
149
136
|
$socket.on("userDeleted", (payload) => {
|
|
150
137
|
const { appSessionId, body, type } = payload
|
|
151
138
|
log.socket("onUserDeleted", {
|
|
@@ -155,9 +142,7 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
|
|
|
155
142
|
})
|
|
156
143
|
$eventbus.emit("socket_deletedUser", { offline: false, body })
|
|
157
144
|
})
|
|
158
|
-
|
|
159
|
-
* projectUpdated
|
|
160
|
-
*/
|
|
145
|
+
|
|
161
146
|
$socket.on("projectUpdated", (payload) => {
|
|
162
147
|
const { appSessionId, body, type } = payload
|
|
163
148
|
|
|
@@ -168,6 +153,7 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
|
|
|
168
153
|
})
|
|
169
154
|
handleServerItem(payload, true, false)
|
|
170
155
|
})
|
|
156
|
+
|
|
171
157
|
$socket.on("projectConfigUpdated", (payload) => {
|
|
172
158
|
const { appSessionId, body, type } = payload
|
|
173
159
|
|
|
@@ -180,9 +166,8 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
|
|
|
180
166
|
})
|
|
181
167
|
|
|
182
168
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
* This usually gets called after we requested a list of all projects which the user has access too
|
|
169
|
+
* Reports or updates the list of available projects.
|
|
170
|
+
* Usually sent after the client requested projects the user has access to.
|
|
186
171
|
*/
|
|
187
172
|
$socket.on("getProjectList", (data) => {
|
|
188
173
|
if (!data || data.length < 1) {
|
|
@@ -192,129 +177,141 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
|
|
|
192
177
|
$store.dispatch({ type: "projects/add", payload: data })
|
|
193
178
|
})
|
|
194
179
|
|
|
180
|
+
// --- Room join / leave ------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
$socket.on("joinFailed", (payload: { roomId?: string; code?: string; message?: string }) => {
|
|
183
|
+
if (!guardActive($socket)) {
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
log.socket("onJoinFailed", payload)
|
|
187
|
+
// Room-scoped failure: transport may still work. A snackbar alone is not enough —
|
|
188
|
+
// surfacing this as a hard error would blank the page while HTTP/socket stay up.
|
|
189
|
+
// TODO: targeted UX once space-switch / permission flows are defined.
|
|
190
|
+
if (!$socket.connected) {
|
|
191
|
+
userNotifier.setServerDisconnected()
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
|
|
195
195
|
/**
|
|
196
|
-
*
|
|
197
|
-
*
|
|
196
|
+
* Entered a project room (new scope). bootstrap=false is a light re-join after reconnect.
|
|
197
|
+
* Work sessions: no project in payload, but compositions/interactionLinks (+ optional token).
|
|
198
198
|
*/
|
|
199
199
|
$socket.on(
|
|
200
200
|
"joinConfirmation",
|
|
201
201
|
(payload: {
|
|
202
|
+
roomId?: string
|
|
203
|
+
bootstrap?: boolean
|
|
202
204
|
project?: { _id: string; account: string }
|
|
203
205
|
projectConfig?: ProjectConfiguration
|
|
204
206
|
compositions?: Composition[]
|
|
205
207
|
interactionLinks?: InteractionLink[]
|
|
206
|
-
token?: string // Work session JWT
|
|
208
|
+
token?: string // Work session JWT — replaces x-work-session-id header
|
|
207
209
|
}) => {
|
|
208
|
-
|
|
210
|
+
if (!guardActive($socket)) {
|
|
211
|
+
return
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const {
|
|
215
|
+
project,
|
|
216
|
+
projectConfig,
|
|
217
|
+
compositions,
|
|
218
|
+
interactionLinks,
|
|
219
|
+
token,
|
|
220
|
+
bootstrap = true,
|
|
221
|
+
} = payload
|
|
209
222
|
|
|
210
|
-
// Check if this is a work session (no project but has compositions/interactionLinks)
|
|
211
223
|
const isWorkSession = !project && compositions && compositions.length > 0
|
|
212
224
|
|
|
213
225
|
if (isWorkSession) {
|
|
214
|
-
// Work session: use session ID as room identifier
|
|
215
226
|
const sessionId = extractSessionIdFromUrl() || "work_session"
|
|
216
227
|
const resolveId = "join_session_" + sessionId
|
|
217
228
|
|
|
218
229
|
log.socket("onJoinConfirmation", "Work session join", payload)
|
|
219
230
|
|
|
220
|
-
// If token is provided, use it for authentication (replaces x-work-session-id header)
|
|
221
231
|
if (token) {
|
|
222
232
|
setHttpToken(token)
|
|
223
|
-
// Clear the x-work-session-id header since we're using token now
|
|
224
233
|
setWorkSessionId(null)
|
|
225
|
-
log.socket("onJoinConfirmation", "Work session token set", {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
$eventbus.once("store_actionResolved_" + resolveId, async (action) => {
|
|
229
|
-
$store.dispatch({
|
|
230
|
-
type: "ui/update",
|
|
231
|
-
payload: { loading: true },
|
|
234
|
+
log.socket("onJoinConfirmation", "Work session token set", {
|
|
235
|
+
hasToken: !!token,
|
|
232
236
|
})
|
|
237
|
+
}
|
|
233
238
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
239
|
+
startProjectBootstrap(
|
|
240
|
+
{ store: $store, eventbus: $eventbus },
|
|
241
|
+
resolveId,
|
|
242
|
+
{
|
|
243
|
+
compositions,
|
|
244
|
+
interactionLinks,
|
|
245
|
+
projectConfig,
|
|
246
|
+
roomId: sessionId,
|
|
247
|
+
afterSetup: async () => {
|
|
248
|
+
// Fullscreen work session — hide chrome; URL stays /:workSession/id={uuid}
|
|
249
|
+
// and the router resolves composition from store.
|
|
250
|
+
$store.dispatch({
|
|
251
|
+
type: "ui/update",
|
|
252
|
+
payload: {
|
|
253
|
+
availableInterfaces: [],
|
|
254
|
+
navigationOpen: false,
|
|
255
|
+
},
|
|
256
|
+
})
|
|
248
257
|
},
|
|
249
|
-
}
|
|
258
|
+
},
|
|
259
|
+
)
|
|
250
260
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
payload: { loading: false },
|
|
254
|
-
})
|
|
261
|
+
return
|
|
262
|
+
}
|
|
255
263
|
|
|
256
|
-
|
|
257
|
-
|
|
264
|
+
if (!project) {
|
|
265
|
+
log.socket("onJoinConfirmation", "no Room")
|
|
266
|
+
return
|
|
267
|
+
}
|
|
258
268
|
|
|
259
|
-
|
|
260
|
-
$store.dispatch({
|
|
261
|
-
type: "projectInit",
|
|
262
|
-
resolveId,
|
|
263
|
-
})
|
|
269
|
+
const roomId = payload.roomId ?? project._id
|
|
264
270
|
|
|
271
|
+
if (!roomId) {
|
|
272
|
+
log.socket("onJoinConfirmation", "no Room id")
|
|
265
273
|
return
|
|
266
274
|
}
|
|
267
275
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
276
|
+
if (bootstrap === false) {
|
|
277
|
+
log.socket(
|
|
278
|
+
"onJoinConfirmation",
|
|
279
|
+
`Joined room without bootstrap: ${roomId}`,
|
|
280
|
+
)
|
|
281
|
+
userNotifier.setServerConnected()
|
|
273
282
|
return
|
|
274
283
|
}
|
|
275
284
|
|
|
276
285
|
log.socket("onJoinConfirmation", payload)
|
|
277
286
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
payload: { loading: true },
|
|
290
|
-
})
|
|
291
|
-
await setupSpace(compositions || [], interactionLinks || [], projectConfig || {})
|
|
292
|
-
|
|
293
|
-
$eventbus.emit("ui/setupProject", roomId)
|
|
294
|
-
})
|
|
295
|
-
// Reset all store states which are project dependent.
|
|
296
|
-
$store.dispatch({
|
|
297
|
-
type: "projectInit",
|
|
298
|
-
resolveId,
|
|
299
|
-
})
|
|
287
|
+
startProjectBootstrap(
|
|
288
|
+
{ store: $store, eventbus: $eventbus },
|
|
289
|
+
"join_" + roomId,
|
|
290
|
+
{
|
|
291
|
+
project,
|
|
292
|
+
compositions,
|
|
293
|
+
interactionLinks,
|
|
294
|
+
projectConfig,
|
|
295
|
+
roomId,
|
|
296
|
+
},
|
|
297
|
+
)
|
|
300
298
|
},
|
|
301
299
|
)
|
|
302
300
|
|
|
303
|
-
|
|
304
|
-
*
|
|
305
|
-
* the backend reports when we leave a room, be it a resonse for our own
|
|
306
|
-
* request or forcefully.
|
|
301
|
+
/**
|
|
302
|
+
* Backend reports leaving a room — response to our request or forced by server.
|
|
307
303
|
*/
|
|
308
304
|
$socket.on("leaveConfirmation", (data) => {
|
|
309
305
|
log.socket("onLeaveConfirmation", data)
|
|
310
306
|
})
|
|
311
307
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
*/
|
|
315
|
-
$socket.on("login", (data) =>
|
|
308
|
+
/** Backend has accepted our auth token on the socket. */
|
|
309
|
+
$socket.on("login", () =>
|
|
316
310
|
log.socket("onLogin", "Socket connection successfully authenticated"),
|
|
317
311
|
)
|
|
312
|
+
|
|
313
|
+
// --- Generic data sync ------------------------------------------------------
|
|
314
|
+
|
|
318
315
|
$socket.on("dataCreated", (payload) => {
|
|
319
316
|
const { body, appSessionId } = payload
|
|
320
317
|
|
|
@@ -325,6 +322,7 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
|
|
|
325
322
|
handleServerItem(payload, true, false)
|
|
326
323
|
$eventbus.emit("socket_dataCreated", body)
|
|
327
324
|
})
|
|
325
|
+
|
|
328
326
|
$socket.on("dataDeleted", (payload) => {
|
|
329
327
|
const { body, appSessionId } = payload
|
|
330
328
|
|
|
@@ -335,10 +333,8 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
|
|
|
335
333
|
handleServerItem(payload, true, false)
|
|
336
334
|
$eventbus.emit("socket_dataDeleted", body)
|
|
337
335
|
})
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
* We received a data object from the backend
|
|
341
|
-
*/
|
|
336
|
+
|
|
337
|
+
/** Incremental data update from the backend. */
|
|
342
338
|
$socket.on("data", (payload) => {
|
|
343
339
|
const { body, appSessionId } = payload
|
|
344
340
|
|
|
@@ -350,9 +346,7 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
|
|
|
350
346
|
$eventbus.emit("socket/data/update", { offline: false, body })
|
|
351
347
|
})
|
|
352
348
|
|
|
353
|
-
/**
|
|
354
|
-
* New deployed frontend version available
|
|
355
|
-
*/
|
|
349
|
+
/** New deployed frontend version available. */
|
|
356
350
|
$socket.on("newVersionAvailable", () => {
|
|
357
351
|
log.socket("onNewVersionAvailable", {})
|
|
358
352
|
userNotifier.newVersionAvailable()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Socket } from "socket.io-client"
|
|
2
|
+
|
|
3
|
+
let activeSocket: Socket | null = null
|
|
4
|
+
|
|
5
|
+
export const setActiveSocket = (socket: Socket) => {
|
|
6
|
+
activeSocket = socket
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const getActiveSocket = (): Socket | null => activeSocket
|
|
10
|
+
|
|
11
|
+
export const isActiveSocket = (socket: Socket): boolean =>
|
|
12
|
+
socket === activeSocket
|
|
13
|
+
|
|
14
|
+
export const teardownSocket = (socket: Socket) => {
|
|
15
|
+
socket.removeAllListeners()
|
|
16
|
+
if (socket.connected) {
|
|
17
|
+
socket.disconnect()
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { QueryState } from "./Store"
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
export type CustomStoreActionParams = Record<string, unknown>
|
|
4
|
+
|
|
5
|
+
/** Callable params source — invoked on each query/write `execute()`. */
|
|
6
|
+
export type QueryParamsSubscriber = () => CustomStoreActionParams
|
|
7
|
+
|
|
8
|
+
/** Core query params: plain snapshot or subscriber (framework-agnostic). */
|
|
9
|
+
export type QueryParamsInput = CustomStoreActionParams | QueryParamsSubscriber
|
|
3
10
|
export type CustomStoreActionOptions = {
|
|
4
11
|
immediate?: boolean
|
|
5
12
|
cb?: Function // function to call when call finised -> execute für getAll
|
|
@@ -12,7 +19,8 @@ export type CustomStoreActionOptions = {
|
|
|
12
19
|
|
|
13
20
|
export type CustomStoreActionsPayload = {
|
|
14
21
|
id?: String // if set this will used as the final namespace giver for the actionId, if not it will be a nanoId
|
|
15
|
-
|
|
22
|
+
/** Plain object or subscriber function — resolved on each `execute()`. */
|
|
23
|
+
params?: QueryParamsInput
|
|
16
24
|
options?: CustomStoreActionOptions
|
|
17
25
|
config?: any
|
|
18
26
|
}
|
|
@@ -12,7 +12,7 @@ import type {
|
|
|
12
12
|
PluginApi,
|
|
13
13
|
} from "@racletteOrchestrator"
|
|
14
14
|
import type { DefineComponent } from "vue"
|
|
15
|
-
import type { UIState,
|
|
15
|
+
import type { UIState, WidgetBase } from "@racletteCore/store/types"
|
|
16
16
|
import type { Query } from "@racletteCore/store/reducers/queries/reducers"
|
|
17
17
|
export type BroadcastChannelDefinition = {
|
|
18
18
|
channel: string
|
|
@@ -65,15 +65,28 @@
|
|
|
65
65
|
<v-main
|
|
66
66
|
class="tw:flex tw:flex-col tw:min-h-0 tw:overflow-y-auto tw:h-screen"
|
|
67
67
|
id="appMainContent"
|
|
68
|
+
:aria-busy="loading"
|
|
68
69
|
>
|
|
69
70
|
<div class="tw:w-full tw-h-1 tw:top-[49px]">
|
|
70
71
|
<v-progress-linear
|
|
71
72
|
:height="2"
|
|
72
73
|
:indeterminate="loading"
|
|
73
74
|
color="primary"
|
|
75
|
+
role="progressbar"
|
|
76
|
+
:aria-hidden="!loading"
|
|
77
|
+
:aria-label="$t('core.app_loading')"
|
|
74
78
|
/>
|
|
75
79
|
</div>
|
|
76
80
|
|
|
81
|
+
<span
|
|
82
|
+
v-if="loading"
|
|
83
|
+
class="tw:sr-only"
|
|
84
|
+
role="status"
|
|
85
|
+
aria-live="polite"
|
|
86
|
+
>
|
|
87
|
+
{{ $t("core.app_loading") }}
|
|
88
|
+
</span>
|
|
89
|
+
|
|
77
90
|
<component v-if="showWelcomeScreen" :is="welcomeScreenComponent" />
|
|
78
91
|
|
|
79
92
|
<template v-else>
|
|
@@ -81,6 +94,7 @@
|
|
|
81
94
|
v-if="widgetsLayout"
|
|
82
95
|
class="tw:min-h-0 tw:pb-0"
|
|
83
96
|
:class="{ '!tw:invisible': loading }"
|
|
97
|
+
:aria-hidden="loading"
|
|
84
98
|
:widgets-layout="widgetsLayout"
|
|
85
99
|
slot-type="page"
|
|
86
100
|
:slot-layout="slotLayout"
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
@layer tailwind-theme;
|
|
2
|
+
@layer tailwind-reset;
|
|
3
|
+
|
|
1
4
|
@layer vuetify-core;
|
|
2
5
|
@layer vuetify-components;
|
|
3
6
|
@layer vuetify-overrides;
|
|
4
7
|
@layer vuetify-utilities;
|
|
5
8
|
|
|
6
|
-
@layer tailwind;
|
|
9
|
+
@layer tailwind-utilities;
|
|
7
10
|
|
|
8
11
|
@layer vuetify-final;
|