@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
|
@@ -5,16 +5,19 @@ import getData from "@racletteCore/lib/data/fetchDataHandler"
|
|
|
5
5
|
import handleServerItem from "./responseTypeHandler"
|
|
6
6
|
import $queryApi from "./queryApi"
|
|
7
7
|
import writeData from "./writeDataHandler"
|
|
8
|
-
import type {
|
|
8
|
+
import type {
|
|
9
|
+
CustomStoreActionsPayload,
|
|
10
|
+
QueryParamsInput,
|
|
11
|
+
} from "@racletteCore/types"
|
|
12
|
+
import { resolveQueryParams } from "@racletteCore/lib/dataHelper"
|
|
9
13
|
import { BehaviorSubject } from "rxjs"
|
|
10
|
-
import {
|
|
14
|
+
import { mergeDeepRight } from "ramda"
|
|
11
15
|
|
|
12
16
|
const $writeDataApi = (
|
|
13
17
|
actionId: String,
|
|
14
18
|
operationDefinition: object,
|
|
15
19
|
{ params = {}, options = {} }: CustomStoreActionsPayload = {},
|
|
16
20
|
) => {
|
|
17
|
-
const _params = clone(params)
|
|
18
21
|
// Create BehaviorSubjects for state management
|
|
19
22
|
const isLoadingSubject$ = new BehaviorSubject<boolean>(false)
|
|
20
23
|
const dataSubject$ = new BehaviorSubject([])
|
|
@@ -25,18 +28,26 @@ const $writeDataApi = (
|
|
|
25
28
|
data$: dataSubject$.asObservable(),
|
|
26
29
|
error$: errorSubject$.asObservable(),
|
|
27
30
|
response$: responseSubject$.asObservable(),
|
|
28
|
-
execute: async (executeParams
|
|
29
|
-
const
|
|
31
|
+
execute: async (executeParams?: QueryParamsInput | FormData | unknown[]) => {
|
|
32
|
+
const isFormData =
|
|
33
|
+
typeof FormData !== "undefined" && executeParams instanceof FormData
|
|
30
34
|
errorSubject$.next(null)
|
|
31
35
|
responseSubject$.next({})
|
|
32
36
|
let queryParams
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
queryParams =
|
|
37
|
+
if (isFormData) {
|
|
38
|
+
queryParams = executeParams
|
|
39
|
+
} else if (Array.isArray(executeParams)) {
|
|
40
|
+
const baseParams = resolveQueryParams(params)
|
|
41
|
+
queryParams = Array.isArray(baseParams)
|
|
42
|
+
? baseParams.concat(executeParams)
|
|
43
|
+
: executeParams
|
|
44
|
+
} else if (executeParams === undefined) {
|
|
45
|
+
queryParams = resolveQueryParams(params)
|
|
38
46
|
} else {
|
|
39
|
-
queryParams = mergeDeepRight(
|
|
47
|
+
queryParams = mergeDeepRight(
|
|
48
|
+
resolveQueryParams(params),
|
|
49
|
+
resolveQueryParams(executeParams as QueryParamsInput),
|
|
50
|
+
)
|
|
40
51
|
}
|
|
41
52
|
|
|
42
53
|
if (options.responseType === "stream") {
|
|
@@ -53,16 +64,16 @@ const $writeDataApi = (
|
|
|
53
64
|
errorSubject$.next(e.response)
|
|
54
65
|
isLoadingSubject$.next(false)
|
|
55
66
|
})
|
|
56
|
-
responseSubject$.next(resultData?.response)
|
|
57
67
|
|
|
58
68
|
// Update both boolean and observable on success
|
|
69
|
+
responseSubject$.next(resultData?.response)
|
|
59
70
|
isLoadingSubject$.next(false)
|
|
60
71
|
|
|
61
72
|
// Update data if resultData is available
|
|
62
|
-
if (resultData?.
|
|
63
|
-
const dataArray = Array.isArray(resultData.
|
|
64
|
-
? resultData.
|
|
65
|
-
: [resultData.
|
|
73
|
+
if (resultData?.response) {
|
|
74
|
+
const dataArray = Array.isArray(resultData.response)
|
|
75
|
+
? resultData.response
|
|
76
|
+
: [resultData.response]
|
|
66
77
|
dataSubject$.next(dataArray)
|
|
67
78
|
}
|
|
68
79
|
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
* data api for the store
|
|
3
3
|
*/
|
|
4
4
|
import { getData } from "@racletteCore/lib/data/dataApi"
|
|
5
|
-
import {
|
|
5
|
+
import { resolveQueryParams } from "@racletteCore/lib/dataHelper"
|
|
6
6
|
import { $store, $eventbus } from "@racletteCore"
|
|
7
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
CustomStoreActionsPayload,
|
|
9
|
+
QueryParamsInput,
|
|
10
|
+
} from "@racletteCore/types"
|
|
8
11
|
import { BehaviorSubject } from "rxjs"
|
|
9
|
-
import { mergeDeepRight
|
|
12
|
+
import { mergeDeepRight } from "ramda"
|
|
10
13
|
|
|
11
14
|
const callbacks = {}
|
|
12
15
|
const queryApis = {}
|
|
@@ -22,7 +25,6 @@ export const $queryApi = (
|
|
|
22
25
|
}
|
|
23
26
|
const errorSubject$ = new BehaviorSubject(null)
|
|
24
27
|
const responseSubject$ = new BehaviorSubject({})
|
|
25
|
-
const cleanedParams = fixQueryParamTypes(params)
|
|
26
28
|
// check if we have a query fullfilling the needs of the one requested
|
|
27
29
|
// here. If not we will update the one with this actionId
|
|
28
30
|
let actionType = "queries/add"
|
|
@@ -39,7 +41,10 @@ export const $queryApi = (
|
|
|
39
41
|
payload: {
|
|
40
42
|
actionId,
|
|
41
43
|
target,
|
|
42
|
-
|
|
44
|
+
// TODO: subscriber params are re-resolved on execute(), but this registration
|
|
45
|
+
// snapshot may leave store query params stale until the next execute()
|
|
46
|
+
// we should check the query behaviour for the store and how to deal with changing queries
|
|
47
|
+
params: resolveQueryParams(params),
|
|
43
48
|
options,
|
|
44
49
|
config,
|
|
45
50
|
},
|
|
@@ -66,11 +71,15 @@ export const $queryApi = (
|
|
|
66
71
|
responseSubject$.complete()
|
|
67
72
|
errorSubject$.complete()
|
|
68
73
|
},
|
|
69
|
-
execute: async (executeParams
|
|
70
|
-
const _executeParams = clone(executeParams)
|
|
74
|
+
execute: async (executeParams?: QueryParamsInput) => {
|
|
71
75
|
errorSubject$.next(null)
|
|
72
76
|
responseSubject$.next({})
|
|
73
|
-
|
|
77
|
+
// `params` may be a subscriber () => {...} from the orchestrator — always resolve here.
|
|
78
|
+
const baseParams = resolveQueryParams(params)
|
|
79
|
+
const queryParams =
|
|
80
|
+
executeParams === undefined
|
|
81
|
+
? baseParams
|
|
82
|
+
: mergeDeepRight(baseParams, resolveQueryParams(executeParams))
|
|
74
83
|
await $store.dispatch({
|
|
75
84
|
type: "queries/update",
|
|
76
85
|
payload: {
|
|
@@ -7,6 +7,15 @@ import * as userNotifier from "@racletteCore/lib/userNotifier"
|
|
|
7
7
|
import { $store, markDataAsModified } from "@racletteCore"
|
|
8
8
|
|
|
9
9
|
const handledRequests = {}
|
|
10
|
+
|
|
11
|
+
const resolveDeletedIds = (data) => {
|
|
12
|
+
if (data.deleted?.length) {
|
|
13
|
+
return data.deleted
|
|
14
|
+
}
|
|
15
|
+
return (data.items || [])
|
|
16
|
+
.map((item) => (typeof item === "string" ? item : item?._id))
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
}
|
|
10
19
|
const hasBeenHandled = (requestId, appRequestCode) => {
|
|
11
20
|
if (requestId === "system") {
|
|
12
21
|
return false
|
|
@@ -137,13 +146,13 @@ const handler = {
|
|
|
137
146
|
requestId,
|
|
138
147
|
data,
|
|
139
148
|
})
|
|
140
|
-
const
|
|
141
|
-
$store.dispatch({ type: "data/hardDelete", payload:
|
|
149
|
+
const deletedIds = resolveDeletedIds(data)
|
|
150
|
+
$store.dispatch({ type: "data/hardDelete", payload: deletedIds })
|
|
142
151
|
if (notify) {
|
|
143
|
-
userNotifier.dataDelete(isOwnRequest, isSystemAction,
|
|
152
|
+
userNotifier.dataDelete(isOwnRequest, isSystemAction, data.items || [])
|
|
144
153
|
}
|
|
145
154
|
|
|
146
|
-
return
|
|
155
|
+
return deletedIds
|
|
147
156
|
},
|
|
148
157
|
dataHardDeleted: (
|
|
149
158
|
data,
|
|
@@ -162,12 +171,12 @@ const handler = {
|
|
|
162
171
|
requestId,
|
|
163
172
|
data,
|
|
164
173
|
})
|
|
165
|
-
const
|
|
166
|
-
$store.dispatch({ type: "data/hardDelete", payload:
|
|
174
|
+
const deletedIds = resolveDeletedIds(data)
|
|
175
|
+
$store.dispatch({ type: "data/hardDelete", payload: deletedIds })
|
|
167
176
|
if (notify) {
|
|
168
|
-
userNotifier.dataDelete(isOwnRequest, isSystemAction,
|
|
177
|
+
userNotifier.dataDelete(isOwnRequest, isSystemAction, data.items || [])
|
|
169
178
|
}
|
|
170
|
-
return
|
|
179
|
+
return deletedIds
|
|
171
180
|
},
|
|
172
181
|
dataMove: (
|
|
173
182
|
data,
|
|
@@ -73,7 +73,9 @@ const storeActions = {
|
|
|
73
73
|
}
|
|
74
74
|
const getAxiosPromise = async (method, target, source, config) => {
|
|
75
75
|
if (method === "delete") {
|
|
76
|
-
|
|
76
|
+
const deleteConfig =
|
|
77
|
+
source != null ? { ...config, data: source } : config
|
|
78
|
+
return await $api.axios.delete(target, deleteConfig)
|
|
77
79
|
}
|
|
78
80
|
return await $api.axios[method](target, source, config)
|
|
79
81
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
*/
|
|
5
5
|
import { clone, path } from "ramda"
|
|
6
|
+
import type { QueryParamsInput } from "@racletteCore/types"
|
|
7
|
+
import { isDevelopmentMode } from "@racletteCore/helpers/devMode"
|
|
6
8
|
import log from "@racletteCore/lib/logger"
|
|
7
9
|
import { $eventbus } from "@racletteCore/main"
|
|
8
10
|
import { $store } from "@racletteCore/main"
|
|
@@ -225,6 +227,10 @@ const replaceLast = (str, pattern, replacement) => {
|
|
|
225
227
|
*
|
|
226
228
|
*/
|
|
227
229
|
const removeCacheAttributes = (item) => {
|
|
230
|
+
if (typeof FormData !== "undefined" && item instanceof FormData) {
|
|
231
|
+
return item
|
|
232
|
+
}
|
|
233
|
+
|
|
228
234
|
const _item = clone(item)
|
|
229
235
|
|
|
230
236
|
if (_item.__requestedFor) {
|
|
@@ -417,25 +423,57 @@ const getValueFromNestedAttributeString = (attrString, item) => {
|
|
|
417
423
|
const isAnswerToOwnRequest = (appSessionId) =>
|
|
418
424
|
appSessionId == $store.appSessionId
|
|
419
425
|
|
|
420
|
-
const
|
|
421
|
-
const fixedParams = clone(params)
|
|
426
|
+
const NUMERIC_QUERY_OPTIONS = ["from", "skip", "size", "limit"] as const
|
|
422
427
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
fixedParams.options.size = parseInt(params.options.size)
|
|
428
|
+
/** Clone query params and coerce `options.from|skip|size|limit` to integers (often strings from URLs/forms). */
|
|
429
|
+
const fixQueryParamTypes = (params: Record<string, unknown> = {}) => {
|
|
430
|
+
const fixedParams = clone(params) as Record<string, unknown>
|
|
431
|
+
const options = fixedParams.options
|
|
432
|
+
|
|
433
|
+
if (options == null || typeof options !== "object" || Array.isArray(options)) {
|
|
434
|
+
return fixedParams
|
|
431
435
|
}
|
|
432
|
-
|
|
433
|
-
|
|
436
|
+
|
|
437
|
+
const opts = options as Record<string, unknown>
|
|
438
|
+
for (const key of NUMERIC_QUERY_OPTIONS) {
|
|
439
|
+
if (opts[key] != null) {
|
|
440
|
+
opts[key] = parseInt(String(opts[key]), 10)
|
|
441
|
+
}
|
|
434
442
|
}
|
|
435
443
|
|
|
436
444
|
return fixedParams
|
|
437
445
|
}
|
|
438
446
|
|
|
447
|
+
const isPlainParamsRecord = (
|
|
448
|
+
value: unknown,
|
|
449
|
+
): value is Record<string, unknown> =>
|
|
450
|
+
value != null && typeof value === "object" && !Array.isArray(value)
|
|
451
|
+
|
|
452
|
+
const warnInvalidResolvedQueryParams = (value: unknown): void => {
|
|
453
|
+
if (!isDevelopmentMode()) return
|
|
454
|
+
const kind =
|
|
455
|
+
value == null ? String(value) : Array.isArray(value) ? "array" : typeof value
|
|
456
|
+
log.warning(
|
|
457
|
+
"Invalid query params",
|
|
458
|
+
`Params subscriber resolved to ${kind}, expected a plain object. Using {}.`,
|
|
459
|
+
)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/** Resolve plain params or subscriber, then normalize query param types. */
|
|
463
|
+
const resolveQueryParams = (
|
|
464
|
+
params?: QueryParamsInput,
|
|
465
|
+
): Record<string, unknown> => {
|
|
466
|
+
let resolved: unknown = params
|
|
467
|
+
if (typeof resolved === "function") {
|
|
468
|
+
resolved = resolved()
|
|
469
|
+
}
|
|
470
|
+
if (!isPlainParamsRecord(resolved)) {
|
|
471
|
+
warnInvalidResolvedQueryParams(resolved)
|
|
472
|
+
return fixQueryParamTypes({})
|
|
473
|
+
}
|
|
474
|
+
return fixQueryParamTypes(resolved)
|
|
475
|
+
}
|
|
476
|
+
|
|
439
477
|
const promisifyActionResolve = (resolveId: string, callback?: Function) => {
|
|
440
478
|
const handleCleanup = (result = {}) => {
|
|
441
479
|
if (typeof callback === "function") {
|
|
@@ -473,6 +511,7 @@ export {
|
|
|
473
511
|
debounceHelper,
|
|
474
512
|
isAnswerToOwnRequest,
|
|
475
513
|
fixQueryParamTypes,
|
|
514
|
+
resolveQueryParams,
|
|
476
515
|
fixDataTypesFromObject,
|
|
477
516
|
getItemsFromLookup,
|
|
478
517
|
getItemFromLookup,
|
|
@@ -56,7 +56,7 @@ interface FetchClientInterface {
|
|
|
56
56
|
post(url: string, data?: any, config?: RequestInit): Promise<Response>
|
|
57
57
|
put(url: string, data?: any, config?: RequestInit): Promise<Response>
|
|
58
58
|
patch(url: string, data?: any, config?: RequestInit): Promise<Response>
|
|
59
|
-
delete(url: string, config?: RequestInit): Promise<Response>
|
|
59
|
+
delete(url: string, data?: any, config?: RequestInit): Promise<Response>
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export type { FetchClientInterface }
|
|
@@ -223,9 +223,23 @@ class FetchClient implements FetchClientInterface {
|
|
|
223
223
|
|
|
224
224
|
async delete(
|
|
225
225
|
url: string,
|
|
226
|
+
data?: any,
|
|
226
227
|
config?: RequestInit & { responseType?: string },
|
|
227
228
|
): Promise<Response> {
|
|
228
|
-
|
|
229
|
+
const isFormData =
|
|
230
|
+
typeof FormData !== "undefined" && data instanceof FormData
|
|
231
|
+
const body = data
|
|
232
|
+
? isFormData
|
|
233
|
+
? data
|
|
234
|
+
: JSON.stringify(data)
|
|
235
|
+
: undefined
|
|
236
|
+
const headers = isFormData
|
|
237
|
+
? { ...config?.headers }
|
|
238
|
+
: {
|
|
239
|
+
"Content-Type": "application/json",
|
|
240
|
+
...config?.headers,
|
|
241
|
+
}
|
|
242
|
+
return this.request(url, { ...config, method: "DELETE", body, headers })
|
|
229
243
|
}
|
|
230
244
|
|
|
231
245
|
// Interceptor management (similar to axios)
|
|
@@ -32,6 +32,24 @@ const setServerDisconnected = () => {
|
|
|
32
32
|
})
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
const setServerConnecting = () => {
|
|
36
|
+
$store.dispatch({
|
|
37
|
+
type: "backend/socket/status",
|
|
38
|
+
payload: { status: 2 },
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const setServerConnected = () => {
|
|
43
|
+
$store.dispatch({
|
|
44
|
+
type: "backend/socket/status",
|
|
45
|
+
payload: { status: 1, health: 1 },
|
|
46
|
+
})
|
|
47
|
+
$store.dispatch({
|
|
48
|
+
type: "backend/search/status",
|
|
49
|
+
payload: { status: 1 },
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
35
53
|
const dataCreate = (isAnswerToOwnRequest, isSystemAction, items) => {
|
|
36
54
|
items.forEach((item) => {
|
|
37
55
|
if (isAnswerToOwnRequest) {
|
|
@@ -348,5 +366,7 @@ export {
|
|
|
348
366
|
userConfigUpdate,
|
|
349
367
|
newVersionAvailable,
|
|
350
368
|
setServerDisconnected,
|
|
369
|
+
setServerConnecting,
|
|
370
|
+
setServerConnected,
|
|
351
371
|
checkToken,
|
|
352
372
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { validate } from "uuid"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract work-session id from the current URL.
|
|
5
|
+
* Supports racletteJS route format: /:workSession/id={uuid}
|
|
6
|
+
*/
|
|
7
|
+
export const extractSessionIdFromUrl = (): string | null => {
|
|
8
|
+
const path = window.location.pathname + (window.location.hash || "")
|
|
9
|
+
const workSessionMatch = path.match(/\/:workSession\/id=([^/&#]+)/)
|
|
10
|
+
|
|
11
|
+
if (workSessionMatch?.[1] && validate(workSessionMatch[1])) {
|
|
12
|
+
return workSessionMatch[1]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
@@ -26,8 +26,16 @@ import { AUTH_TOKEN_KEY } from "@racletteCore/setup/sessionHandler"
|
|
|
26
26
|
import $log from "@racletteCore/lib/logger"
|
|
27
27
|
import { awaitTime } from "@racletteCore/lib/dataHelper"
|
|
28
28
|
import type { User } from "@shared/types/core/User.types"
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
checkToken,
|
|
31
|
+
setServerConnecting,
|
|
32
|
+
} from "@racletteCore/lib/userNotifier"
|
|
30
33
|
import configService from "@racletteCore/lib/configService"
|
|
34
|
+
import { isDevelopmentMode } from "@racletteCore/helpers/devMode"
|
|
35
|
+
import {
|
|
36
|
+
setActiveSocket,
|
|
37
|
+
teardownSocket,
|
|
38
|
+
} from "@racletteCore/setup/socketRegistry"
|
|
31
39
|
import {
|
|
32
40
|
registerPlugins,
|
|
33
41
|
registeredPlugins,
|
|
@@ -36,6 +44,12 @@ import {
|
|
|
36
44
|
const SOCKET_URL = getEnvVariable("RACLETTE_SOCKET_URL")
|
|
37
45
|
const API_BASE_URL = getEnvVariable("RACLETTE_SERVER_BASE_URL")
|
|
38
46
|
|
|
47
|
+
/** First warning while waiting for socket bootstrap to finish. */
|
|
48
|
+
const SETUP_PROJECT_WARN_MS = 5_000
|
|
49
|
+
/** Hard timeout in production (dev mode never rejects — easier backend debugging). */
|
|
50
|
+
const SETUP_PROJECT_TIMEOUT_MS = 60_000
|
|
51
|
+
const SETUP_PROJECT_DEV_WARN_INTERVAL_MS = 10_000
|
|
52
|
+
|
|
39
53
|
export type CreateAppResponse = {
|
|
40
54
|
authenticated: boolean
|
|
41
55
|
setupComplete: boolean
|
|
@@ -81,7 +95,6 @@ const createRacletteApp = async (
|
|
|
81
95
|
let user: User | null = null
|
|
82
96
|
await configService.init()
|
|
83
97
|
|
|
84
|
-
// If sessionId is provided, skip authentication
|
|
85
98
|
if (options.requireAuth && !options.sessionId) {
|
|
86
99
|
const { authenticated, error, user: _user, code } = await checkAuthStatus()
|
|
87
100
|
|
|
@@ -102,20 +115,65 @@ const createRacletteApp = async (
|
|
|
102
115
|
|
|
103
116
|
store.dispatch({ type: "user/set", payload: user })
|
|
104
117
|
|
|
105
|
-
|
|
106
|
-
|
|
118
|
+
setServerConnecting()
|
|
119
|
+
|
|
120
|
+
return new Promise<CreateAppResponse>((resolve, reject) => {
|
|
121
|
+
let settled = false
|
|
107
122
|
|
|
108
|
-
|
|
109
|
-
|
|
123
|
+
const onSetupProject = (spaceId: string) => {
|
|
124
|
+
if (settled) {
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
settled = true
|
|
129
|
+
eventbus.removeListener("ui/setupProject", onSetupProject)
|
|
110
130
|
resolve({
|
|
111
131
|
authenticated: !!user || !!options.sessionId,
|
|
112
132
|
setupComplete: true,
|
|
113
133
|
spaceId,
|
|
114
134
|
})
|
|
115
135
|
console.log(chadsayFE("racletteJS is up and running!"))
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
eventbus.on("ui/setupProject", onSetupProject)
|
|
139
|
+
|
|
140
|
+
void (async () => {
|
|
141
|
+
await awaitTime(SETUP_PROJECT_WARN_MS)
|
|
142
|
+
if (settled) {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.warn(
|
|
147
|
+
"[bootstrap] Still waiting for ui/setupProject (socket bootstrap). " +
|
|
148
|
+
"Check server joinConfirmation / projectInit in the network tab.",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
if (isDevelopmentMode()) {
|
|
152
|
+
while (!settled) {
|
|
153
|
+
await awaitTime(SETUP_PROJECT_DEV_WARN_INTERVAL_MS)
|
|
154
|
+
if (settled) {
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
console.warn(
|
|
158
|
+
"[bootstrap] Bootstrap still pending (dev mode — no timeout).",
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
await awaitTime(SETUP_PROJECT_TIMEOUT_MS - SETUP_PROJECT_WARN_MS)
|
|
165
|
+
if (settled) {
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
settled = true
|
|
170
|
+
eventbus.removeListener("ui/setupProject", onSetupProject)
|
|
171
|
+
reject(
|
|
172
|
+
new Error(
|
|
173
|
+
`createRacletteApp timed out after ${SETUP_PROJECT_TIMEOUT_MS / 1000}s waiting for ui/setupProject`,
|
|
174
|
+
),
|
|
175
|
+
)
|
|
176
|
+
})()
|
|
119
177
|
})
|
|
120
178
|
}
|
|
121
179
|
|
|
@@ -162,8 +220,6 @@ const generateLoginCallback =
|
|
|
162
220
|
|
|
163
221
|
store.dispatch({ type: "user/set", payload: response.data.user })
|
|
164
222
|
|
|
165
|
-
eventbus.emit("socket/join/room")
|
|
166
|
-
|
|
167
223
|
// Return success with token & redirect function
|
|
168
224
|
return {
|
|
169
225
|
success: true,
|
|
@@ -263,12 +319,23 @@ const setupDependencies = async (sessionId?: string) => {
|
|
|
263
319
|
configureHttpInterceptors(store)
|
|
264
320
|
apiHealthcheck(store)
|
|
265
321
|
|
|
266
|
-
// socket
|
|
267
|
-
|
|
322
|
+
// socket — replace connection; detach old listeners before disconnect to avoid
|
|
323
|
+
// a stale disconnect handler marking the server offline after the new socket connects
|
|
324
|
+
const previousSocket = socket
|
|
268
325
|
|
|
269
|
-
|
|
270
|
-
|
|
326
|
+
socket = createSocket(SOCKET_URL, appSessionId, { sessionId })
|
|
327
|
+
setActiveSocket(socket)
|
|
328
|
+
setServerConnecting()
|
|
271
329
|
registerSocketClient(socket, eventbus)
|
|
330
|
+
registerSocketEvents(socket, store, eventbus)
|
|
331
|
+
|
|
332
|
+
if (previousSocket) {
|
|
333
|
+
teardownSocket(previousSocket)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (socket.connected) {
|
|
337
|
+
eventbus.emit("socket/join/room")
|
|
338
|
+
}
|
|
272
339
|
|
|
273
340
|
// TODO: improve this code
|
|
274
341
|
corePluginApi = {
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Socket project bootstrap
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Server emits joinConfirmation → client dispatches projectInit (resets project-scoped store slices)
|
|
6
|
+
* 2. projectInit completes → store emits store_actionResolved_{resolveId}
|
|
7
|
+
* 3. onProjectInitResolved runs setupSpace, syncs route compositions, emits ui/setupProject
|
|
8
|
+
*/
|
|
9
|
+
import type { Composition } from "@shared/types/core/Composition.types"
|
|
10
|
+
import type { InteractionLink } from "@shared/types/core/InteractionLink.types"
|
|
11
|
+
import { setupSpace } from "@racletteCore/lib/projectDataHelper"
|
|
12
|
+
import type { ProjectConfiguration } from "../store/types"
|
|
13
|
+
import type EventEmitter from "@racletteCore/lib/eventEmitter"
|
|
14
|
+
import type { RacletteStore } from "@racletteCore"
|
|
15
|
+
|
|
16
|
+
export type ProjectBootstrapPayload = {
|
|
17
|
+
compositions?: Composition[]
|
|
18
|
+
interactionLinks?: InteractionLink[]
|
|
19
|
+
projectConfig?: ProjectConfiguration
|
|
20
|
+
project?: { _id: string; account: string }
|
|
21
|
+
roomId: string
|
|
22
|
+
/** Runs after setupSpace (e.g. work-session fullscreen UI). */
|
|
23
|
+
afterSetup?: () => void | Promise<void>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type BootstrapDeps = {
|
|
27
|
+
store: RacletteStore
|
|
28
|
+
eventbus: EventEmitter
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Registers the post-projectInit handler, then resets project-scoped store state.
|
|
33
|
+
*/
|
|
34
|
+
export const startProjectBootstrap = (
|
|
35
|
+
{ store, eventbus }: BootstrapDeps,
|
|
36
|
+
resolveId: string,
|
|
37
|
+
payload: ProjectBootstrapPayload,
|
|
38
|
+
) => {
|
|
39
|
+
const {
|
|
40
|
+
compositions,
|
|
41
|
+
interactionLinks,
|
|
42
|
+
projectConfig,
|
|
43
|
+
project,
|
|
44
|
+
roomId,
|
|
45
|
+
afterSetup,
|
|
46
|
+
} = payload
|
|
47
|
+
|
|
48
|
+
eventbus.once(`store_actionResolved_${resolveId}`, async () => {
|
|
49
|
+
if (project) {
|
|
50
|
+
store.dispatch({
|
|
51
|
+
type: "project/set",
|
|
52
|
+
payload: project,
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
store.dispatch({
|
|
57
|
+
type: "ui/update",
|
|
58
|
+
payload: { loading: true },
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
// Same setupSpace as logged-in users (landing page detection, etc.)
|
|
62
|
+
await setupSpace(
|
|
63
|
+
compositions || [],
|
|
64
|
+
interactionLinks || [],
|
|
65
|
+
projectConfig || {},
|
|
66
|
+
)
|
|
67
|
+
// Re-apply route composition slots after projectInit cleared ui.page
|
|
68
|
+
eventbus.emit("ui/syncRouteCompositions")
|
|
69
|
+
await afterSetup?.()
|
|
70
|
+
|
|
71
|
+
store.dispatch({
|
|
72
|
+
type: "ui/update",
|
|
73
|
+
payload: { loading: false },
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
eventbus.emit("ui/setupProject", roomId)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
store.dispatch({
|
|
80
|
+
type: "projectInit",
|
|
81
|
+
resolveId,
|
|
82
|
+
})
|
|
83
|
+
}
|