@raclettejs/core 0.1.34 → 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 +30 -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/provision/entrypoint.sh +2 -2
- package/services/frontend/src/core/helpers/devMode.ts +1 -3
- 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/logger.ts +2 -3
- 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/store/index.ts +2 -4
- 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 +15 -1
- 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/composables/useWidgetLifecycle.ts +2 -4
- 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/docker-compose.yml +1 -4
- 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
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raclettejs/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36-canary.0",
|
|
4
4
|
"description": "racletteJS core package",
|
|
5
5
|
"repository": "https://gitlab.com/raclettejs/core",
|
|
6
6
|
"author": "Pacifico Digital Explorations GmbH <info@raclettejs.com>",
|
|
7
7
|
"license": "AGPL-3.0-only",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"scripts": {
|
|
10
|
-
":install": "yarn install",
|
|
11
10
|
":install:services": "yarn --cwd ./services/backend install && yarn --cwd ./services/frontend install",
|
|
12
11
|
"build": "node build.mjs || true",
|
|
13
12
|
"dev": "node watch.mjs",
|
|
@@ -17,7 +16,6 @@
|
|
|
17
16
|
"lint": "eslint",
|
|
18
17
|
"lint:grouped": "FILTER_RULE=$RULE eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --format ./dev/eslint/groupByRuleFormatter.cjs",
|
|
19
18
|
"lint:fix": "eslint --fix",
|
|
20
|
-
"prepare": "husky",
|
|
21
19
|
"prepublishOnly": "yarn run build && yarn run sync",
|
|
22
20
|
"publish:patch": "yarn version --patch && yarn publish",
|
|
23
21
|
"sync": "node ./scripts/sync-exports.js"
|
|
@@ -30,6 +28,9 @@
|
|
|
30
28
|
"types": "./types/index.ts",
|
|
31
29
|
"files": [
|
|
32
30
|
"services",
|
|
31
|
+
"!services/**/.yarn",
|
|
32
|
+
"!services/**/node_modules",
|
|
33
|
+
"!services/**/.yarnrc.yml",
|
|
33
34
|
"bin",
|
|
34
35
|
"dist",
|
|
35
36
|
"types",
|
|
@@ -82,7 +83,7 @@
|
|
|
82
83
|
"@emnapi/core": "1.9.2",
|
|
83
84
|
"@emnapi/runtime": "1.9.2",
|
|
84
85
|
"@eslint/js": "9.35.0",
|
|
85
|
-
"@raclettejs/types": "
|
|
86
|
+
"@raclettejs/types": "workspace:",
|
|
86
87
|
"@types/fs-extra": "11.0.4",
|
|
87
88
|
"@types/js-yaml": "4.0.9",
|
|
88
89
|
"@types/node": "25.5.0",
|
|
@@ -95,7 +96,6 @@
|
|
|
95
96
|
"eslint-plugin-prettier": "5.5.5",
|
|
96
97
|
"eslint-plugin-vue": "9.33.0",
|
|
97
98
|
"globals": "17.4.0",
|
|
98
|
-
"husky": "9.1.7",
|
|
99
99
|
"prettier": "3.8.1",
|
|
100
100
|
"typescript": "5.9.3",
|
|
101
101
|
"typescript-eslint": "8.59.2",
|
package/services/.dockerignore
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { FastifyInstance } from "fastify"
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
PluginOptions,
|
|
4
|
+
RegisterForFrontendGenerationOptions,
|
|
5
|
+
} from "../pluginTypes"
|
|
3
6
|
import type {
|
|
4
7
|
PluginRegistration,
|
|
5
8
|
TrackedRoute,
|
|
@@ -137,17 +140,14 @@ class FrontendConfigRegistry {
|
|
|
137
140
|
// Plugin Registration Functions (Called by Plugins)
|
|
138
141
|
// ========================================
|
|
139
142
|
|
|
143
|
+
export type {
|
|
144
|
+
RegisterForFrontendGenerationOptions,
|
|
145
|
+
RegisterForFrontendGenerationInput,
|
|
146
|
+
} from "../pluginTypes"
|
|
147
|
+
|
|
140
148
|
export const registerPluginForFrontendGeneration = (
|
|
141
149
|
pluginOptions: PluginOptions,
|
|
142
|
-
options?:
|
|
143
|
-
routePrefix?: string
|
|
144
|
-
entityMapping?: Record<string, string>
|
|
145
|
-
customConfig?: {
|
|
146
|
-
broadcast?: boolean
|
|
147
|
-
channels?: Record<string, string>
|
|
148
|
-
operationMapping?: Record<string, string>
|
|
149
|
-
}
|
|
150
|
-
},
|
|
150
|
+
options?: RegisterForFrontendGenerationOptions,
|
|
151
151
|
): void => {
|
|
152
152
|
const registry = FrontendConfigRegistry.getInstance()
|
|
153
153
|
registry.registerPlugin(pluginOptions, options)
|
|
@@ -306,7 +306,8 @@ const parseRouteInfo = (
|
|
|
306
306
|
.filter((segment) => segment.startsWith(":"))
|
|
307
307
|
.map((param) => param.substring(1))
|
|
308
308
|
|
|
309
|
-
const operationName =
|
|
309
|
+
const operationName =
|
|
310
|
+
route.config?.operationName ?? getOperationName(method, operationSegments)
|
|
310
311
|
|
|
311
312
|
return {
|
|
312
313
|
method,
|
|
@@ -8,7 +8,11 @@ import type {
|
|
|
8
8
|
IntervalTaskOptions,
|
|
9
9
|
} from "@/types"
|
|
10
10
|
import type { CrudHandlers } from "@c/crud/crudTypes"
|
|
11
|
-
import type {
|
|
11
|
+
import type {
|
|
12
|
+
PluginFastifyInstance,
|
|
13
|
+
PluginOptions,
|
|
14
|
+
RegisterForFrontendGenerationInput,
|
|
15
|
+
} from "./pluginTypes"
|
|
12
16
|
import mongoose from "mongoose"
|
|
13
17
|
import type EventEmitter from "@c/events/eventEmitter"
|
|
14
18
|
import { type Listener } from "@c/events/eventEmitter"
|
|
@@ -204,8 +208,29 @@ export const createPluginFastifyInstance = (
|
|
|
204
208
|
registerPluginContract(pluginOptions.key, contractData)
|
|
205
209
|
},
|
|
206
210
|
|
|
207
|
-
registerForFrontendGeneration: (
|
|
208
|
-
|
|
211
|
+
registerForFrontendGeneration: (
|
|
212
|
+
options?: RegisterForFrontendGenerationInput,
|
|
213
|
+
) => {
|
|
214
|
+
const fg = pluginOptions._loaderInternal?.frontendGeneration
|
|
215
|
+
if (!fg) {
|
|
216
|
+
if (options === false) {
|
|
217
|
+
return
|
|
218
|
+
}
|
|
219
|
+
registerPluginForFrontendGeneration(
|
|
220
|
+
pluginOptions,
|
|
221
|
+
options === undefined ? {} : options,
|
|
222
|
+
)
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
if (options === false) {
|
|
226
|
+
fg.optedOut = true
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
fg.explicitCall = true
|
|
230
|
+
registerPluginForFrontendGeneration(
|
|
231
|
+
pluginOptions,
|
|
232
|
+
options === undefined ? {} : options,
|
|
233
|
+
)
|
|
209
234
|
},
|
|
210
235
|
|
|
211
236
|
cache: pluginCacheService,
|
|
@@ -5,6 +5,7 @@ import type { FastifyInstance } from "fastify"
|
|
|
5
5
|
import fs from "fs"
|
|
6
6
|
import path from "path"
|
|
7
7
|
import { createPluginFastifyInstance } from "./pluginFastifyInstance"
|
|
8
|
+
import { registerPluginForFrontendGeneration } from "@c/pluginSystem/configGenerator"
|
|
8
9
|
import configService from "@c/config/configService"
|
|
9
10
|
import { slugify } from "@shared/helper/pluginHelper"
|
|
10
11
|
import { fileURLToPath } from "url"
|
|
@@ -759,6 +760,9 @@ const registerCollectedPlugins = async (
|
|
|
759
760
|
routePrefix,
|
|
760
761
|
absolutePath: plugin.pluginPath,
|
|
761
762
|
frontendAbsolutePath: plugin.frontendPath,
|
|
763
|
+
_loaderInternal: {
|
|
764
|
+
frontendGeneration: { explicitCall: false, optedOut: false },
|
|
765
|
+
},
|
|
762
766
|
}
|
|
763
767
|
|
|
764
768
|
// Dynamically import the plugin module
|
|
@@ -786,10 +790,15 @@ const registerCollectedPlugins = async (
|
|
|
786
790
|
const customFastify = createPluginFastifyInstance(fastify, pluginOptions)
|
|
787
791
|
|
|
788
792
|
// Register the plugin, passing the restricted instance and options
|
|
789
|
-
await fastify.register(() => {
|
|
790
|
-
|
|
793
|
+
await fastify.register(async () => {
|
|
794
|
+
await pluginFunction(customFastify, pluginOptions)
|
|
791
795
|
})
|
|
792
796
|
|
|
797
|
+
const fg = pluginOptions._loaderInternal?.frontendGeneration
|
|
798
|
+
if (fg && !fg.optedOut && !fg.explicitCall) {
|
|
799
|
+
registerPluginForFrontendGeneration(pluginOptions, {})
|
|
800
|
+
}
|
|
801
|
+
|
|
793
802
|
logPluginSuccess(
|
|
794
803
|
fastify,
|
|
795
804
|
plugin.type,
|
|
@@ -20,6 +20,18 @@ export enum PluginType {
|
|
|
20
20
|
APP = "app",
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Internal state the plugin loader attaches to `PluginOptions` while a backend
|
|
25
|
+
* plugin boots. Extend with additional nested sections as new loader features
|
|
26
|
+
* need post-plugin hooks.
|
|
27
|
+
*/
|
|
28
|
+
export type PluginLoaderInternalState = {
|
|
29
|
+
frontendGeneration: {
|
|
30
|
+
explicitCall: boolean
|
|
31
|
+
optedOut: boolean
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
23
35
|
export type PluginOptions = {
|
|
24
36
|
key: string
|
|
25
37
|
pluginName: string
|
|
@@ -33,8 +45,32 @@ export type PluginOptions = {
|
|
|
33
45
|
registerEvents?: (io: SocketServer, eventbus: any) => void
|
|
34
46
|
createRooms?: (io: SocketServer) => void
|
|
35
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Loader-owned mutable bookkeeping (not part of the public plugin API).
|
|
50
|
+
* Plugin code should ignore; the loader reads this after `index.ts` returns.
|
|
51
|
+
*/
|
|
52
|
+
_loaderInternal?: PluginLoaderInternalState
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Options when a plugin customizes frontend generation metadata. */
|
|
56
|
+
export type RegisterForFrontendGenerationOptions = {
|
|
57
|
+
routePrefix?: string
|
|
58
|
+
entityMapping?: Record<string, string>
|
|
59
|
+
customConfig?: {
|
|
60
|
+
broadcast?: boolean
|
|
61
|
+
channels?: Record<string, string>
|
|
62
|
+
operationMapping?: Record<string, string>
|
|
63
|
+
}
|
|
36
64
|
}
|
|
37
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Argument to `fastify.registerForFrontendGeneration`.
|
|
68
|
+
* Pass `false` to opt out of frontend generation registration for this plugin.
|
|
69
|
+
*/
|
|
70
|
+
export type RegisterForFrontendGenerationInput =
|
|
71
|
+
| RegisterForFrontendGenerationOptions
|
|
72
|
+
| false
|
|
73
|
+
|
|
38
74
|
export type PluginFastifyInstance = {
|
|
39
75
|
/* ROUTES */
|
|
40
76
|
register: (
|
|
@@ -87,7 +123,9 @@ export type PluginFastifyInstance = {
|
|
|
87
123
|
eventbus: EventEmitter
|
|
88
124
|
|
|
89
125
|
registerPluginContract: (contractData: PluginContract) => void
|
|
90
|
-
registerForFrontendGeneration: (
|
|
126
|
+
registerForFrontendGeneration: (
|
|
127
|
+
options?: RegisterForFrontendGenerationInput,
|
|
128
|
+
) => void
|
|
91
129
|
|
|
92
130
|
fastify: FastifyInstance
|
|
93
131
|
custom: Record<any, any>
|
|
@@ -9,7 +9,7 @@ export const createSystemEventHandlers = (fastify: FastifyInstance) => {
|
|
|
9
9
|
// TODO rework this to handle spaces instead of project and in the future document rooms -> y.js?
|
|
10
10
|
// Is this even used? SocketManager also has wss.emit
|
|
11
11
|
fastify.log.debug(
|
|
12
|
-
`[Socket] sendToChannel ${key} to room ${payload.project} and
|
|
12
|
+
`[Socket] sendToChannel ${key} to room ${payload.project}${payload.workSessionId ? ' and workSession ' + payload.workSessionId : ''}`,
|
|
13
13
|
)
|
|
14
14
|
if (payload.project) {
|
|
15
15
|
wss.in(payload.project).emit(key, payload)
|
|
@@ -22,15 +22,15 @@ export const createSystemEventHandlers = (fastify: FastifyInstance) => {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
return {
|
|
25
|
-
|
|
25
|
+
broadcastDataUpdated: (payload: FrontendPayload<unknown>): void => {
|
|
26
26
|
fastify.log.info(`[Socket] Push onData: ${payload.appSessionId}`)
|
|
27
27
|
sendToChannel(payload, "data")
|
|
28
28
|
},
|
|
29
|
-
|
|
29
|
+
broadcastDataCreated: (payload: FrontendPayload<unknown>): void => {
|
|
30
30
|
fastify.log.info(`[Socket] Push onCreate: ${payload.appSessionId}`)
|
|
31
31
|
sendToChannel(payload, "dataCreated")
|
|
32
32
|
},
|
|
33
|
-
|
|
33
|
+
broadcastDataDeleted: (payload: FrontendPayload<unknown>): void => {
|
|
34
34
|
fastify.log.info(`[Socket] Push onDelete: ${payload.appSessionId}`)
|
|
35
35
|
sendToChannel(payload, "dataDeleted")
|
|
36
36
|
},
|
|
@@ -8,8 +8,7 @@ import { buildWorkSessionJoinPayload } from "./workSessionJoinPayload"
|
|
|
8
8
|
|
|
9
9
|
interface JoinPayload {
|
|
10
10
|
roomId?: string
|
|
11
|
-
|
|
12
|
-
tenantId?: string
|
|
11
|
+
bootstrap?: boolean
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
interface ProjectResponse {
|
|
@@ -29,7 +28,25 @@ const isSocketAuthenticated = (
|
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
/**
|
|
32
|
-
*
|
|
31
|
+
* Guard for explicit room joins. Server-assigned readMain joins skip this.
|
|
32
|
+
*/
|
|
33
|
+
const assertCanJoinProject = async (
|
|
34
|
+
fastify: FastifyInstance,
|
|
35
|
+
socket: Socket,
|
|
36
|
+
spaceId: string,
|
|
37
|
+
): Promise<ProjectResponse> => {
|
|
38
|
+
const project = await loadProject(fastify, spaceId)
|
|
39
|
+
if (!project) {
|
|
40
|
+
throw new Error("Space not found")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// TODO: here we add the guard if a user is allowed to join a specific space or not
|
|
44
|
+
|
|
45
|
+
throw new Error("Not allowed to join this space")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Load project by ID or assign main project when roomId is omitted
|
|
33
50
|
*/
|
|
34
51
|
const loadProject = async (
|
|
35
52
|
fastify: FastifyInstance,
|
|
@@ -379,34 +396,66 @@ const handleAuthenticatedUserJoin = async (
|
|
|
379
396
|
socket: Socket,
|
|
380
397
|
payload: JoinPayload,
|
|
381
398
|
): Promise<void> => {
|
|
382
|
-
const
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
399
|
+
const explicitSpaceId = payload.roomId
|
|
400
|
+
const bootstrap = payload.bootstrap ?? !explicitSpaceId
|
|
401
|
+
let spaceId = explicitSpaceId
|
|
402
|
+
let project: ProjectResponse | null = null
|
|
403
|
+
|
|
404
|
+
if (explicitSpaceId) {
|
|
405
|
+
try {
|
|
406
|
+
project = await assertCanJoinProject(fastify, socket, explicitSpaceId)
|
|
407
|
+
spaceId = explicitSpaceId
|
|
408
|
+
} catch (error) {
|
|
409
|
+
const message =
|
|
410
|
+
error instanceof Error ? error.message : "Access denied"
|
|
411
|
+
const code = message === "Space not found" ? "NOT_FOUND" : "FORBIDDEN"
|
|
412
|
+
socket.emit("joinFailed", {
|
|
413
|
+
roomId: explicitSpaceId,
|
|
414
|
+
code,
|
|
415
|
+
message,
|
|
416
|
+
})
|
|
417
|
+
return
|
|
418
|
+
}
|
|
419
|
+
} else {
|
|
420
|
+
// per default we let every user join the default space for the app
|
|
421
|
+
project = await loadProject(fastify)
|
|
422
|
+
if (!project) {
|
|
423
|
+
socket.emit("joinFailed", {
|
|
424
|
+
code: "NOT_FOUND",
|
|
425
|
+
message: "No default space found. Something seems wrongly configured!",
|
|
426
|
+
})
|
|
427
|
+
return
|
|
428
|
+
}
|
|
429
|
+
spaceId = project._id
|
|
394
430
|
}
|
|
395
431
|
|
|
396
|
-
socket.join(
|
|
397
|
-
socket.data
|
|
398
|
-
|
|
432
|
+
await socket.join(spaceId)
|
|
433
|
+
if (!socket.data.projects) {
|
|
434
|
+
socket.data.projects = new Set<string>()
|
|
435
|
+
}
|
|
436
|
+
socket.data.projects.add(spaceId)
|
|
399
437
|
|
|
400
|
-
|
|
401
|
-
const decodedToken = socket.decoded_token as any
|
|
438
|
+
const decodedToken = socket.decoded_token as { _id?: string }
|
|
402
439
|
const userId = decodedToken?._id
|
|
403
|
-
if (userId) {
|
|
440
|
+
if (bootstrap && userId) {
|
|
404
441
|
await joinOwnedWorkSessions(fastify, socket, userId)
|
|
405
442
|
}
|
|
406
443
|
|
|
407
|
-
const responseData =
|
|
408
|
-
|
|
409
|
-
|
|
444
|
+
const responseData =
|
|
445
|
+
bootstrap && project
|
|
446
|
+
? await buildAuthenticatedUserResponse(fastify, project)
|
|
447
|
+
: project
|
|
448
|
+
? { project }
|
|
449
|
+
: {}
|
|
450
|
+
|
|
451
|
+
socket.emit("joinConfirmation", {
|
|
452
|
+
...responseData,
|
|
453
|
+
roomId: spaceId,
|
|
454
|
+
bootstrap,
|
|
455
|
+
})
|
|
456
|
+
fastify.log.info(
|
|
457
|
+
`[Socket] Socket ${socket.id} joined room ${spaceId}, rooms: ${[...socket.rooms].join(", ")}`,
|
|
458
|
+
)
|
|
410
459
|
}
|
|
411
460
|
|
|
412
461
|
/**
|
|
@@ -418,20 +467,18 @@ const registerSocketEvents = (fastify: FastifyInstance): void => {
|
|
|
418
467
|
const requiresAuth = configService.isAuthRequired()
|
|
419
468
|
|
|
420
469
|
wss.on("connection", (socket: Socket) => {
|
|
421
|
-
const isAuthenticated = isSocketAuthenticated(socket, requiresAuth)
|
|
422
|
-
|
|
423
470
|
fastify.log.info(`[Socket] Connected ${socket.id}`)
|
|
424
471
|
socket.emit("socketEstablished", { id: socket.id })
|
|
425
472
|
|
|
426
|
-
if (!
|
|
473
|
+
if (!isSocketAuthenticated(socket, requiresAuth) && requiresAuth) {
|
|
427
474
|
fastify.log.warn(`[Socket] Unauthenticated connection ${socket.id}`)
|
|
428
475
|
socket.emit("authenticationRequired", {
|
|
429
476
|
message: "Authentication required",
|
|
430
477
|
})
|
|
431
478
|
}
|
|
432
479
|
|
|
433
|
-
socket.on("join", async (payload: JoinPayload) => {
|
|
434
|
-
if (requiresAuth && !
|
|
480
|
+
socket.on("join", async (payload: JoinPayload = {}) => {
|
|
481
|
+
if (requiresAuth && !isSocketAuthenticated(socket, requiresAuth)) {
|
|
435
482
|
socket.emit("authenticationFailed", {
|
|
436
483
|
message: "Authentication required to join room",
|
|
437
484
|
})
|
|
@@ -451,6 +498,7 @@ const registerSocketEvents = (fastify: FastifyInstance): void => {
|
|
|
451
498
|
|
|
452
499
|
socket.on("leave", (room: string) => {
|
|
453
500
|
socket.leave(room)
|
|
501
|
+
socket.data.projects?.delete(room)
|
|
454
502
|
socket.emit("leaveConfirmation", room)
|
|
455
503
|
wss.in(room).emit("memberLeft", {
|
|
456
504
|
room,
|
|
@@ -465,9 +513,9 @@ const registerSocketEvents = (fastify: FastifyInstance): void => {
|
|
|
465
513
|
|
|
466
514
|
// Register system event handlers
|
|
467
515
|
const systemEvents = {
|
|
468
|
-
broadcastDataUpdated: eventbusHandler.
|
|
469
|
-
broadcastDataCreated: eventbusHandler.
|
|
470
|
-
broadcastDataDeleted: eventbusHandler.
|
|
516
|
+
broadcastDataUpdated: eventbusHandler.broadcastDataUpdated,
|
|
517
|
+
broadcastDataCreated: eventbusHandler.broadcastDataCreated,
|
|
518
|
+
broadcastDataDeleted: eventbusHandler.broadcastDataDeleted,
|
|
471
519
|
broadcastTagCreated: eventbusHandler.broadcastTagCreated,
|
|
472
520
|
broadcastTagDeleted: eventbusHandler.broadcastTagDeleted,
|
|
473
521
|
broadcastUserCreated: eventbusHandler.broadcastUserCreated,
|
|
@@ -2,6 +2,12 @@ import type { DefaultEventsMap, EventsMap } from "socket.io/dist/typed-events"
|
|
|
2
2
|
import type { VerifyPayloadType } from "@fastify/jwt"
|
|
3
3
|
|
|
4
4
|
declare module "socket.io" {
|
|
5
|
+
interface SocketData {
|
|
6
|
+
projects?: Set<string>
|
|
7
|
+
ownedSessions?: string[]
|
|
8
|
+
session?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
interface Socket<
|
|
6
12
|
ListenEvents extends EventsMap = DefaultEventsMap,
|
|
7
13
|
EmitEvents extends EventsMap = ListenEvents,
|
|
@@ -81,7 +81,10 @@ export const generateSharedSchemas = async (sharedDir: string) => {
|
|
|
81
81
|
|
|
82
82
|
// Generate index files
|
|
83
83
|
await generateCoreTypesIndex(sharedDir)
|
|
84
|
-
|
|
84
|
+
if (process.env.RACLETTE_CORE_ABSOLUTE_PATH?.includes("node_modules")) {
|
|
85
|
+
// only generate this for non-linked core to prevent mixup of app content in code
|
|
86
|
+
await generatePluginTypesIndex(sharedDir)
|
|
87
|
+
}
|
|
85
88
|
await generateMainTypesIndex(sharedDir)
|
|
86
89
|
}
|
|
87
90
|
|
|
@@ -268,7 +271,10 @@ const generateCoreTypesIndex = async (sharedDir: string) => {
|
|
|
268
271
|
.join("\n")
|
|
269
272
|
|
|
270
273
|
// Write index file
|
|
271
|
-
await fs.writeFile(
|
|
274
|
+
await fs.writeFile(
|
|
275
|
+
path.join(coreDir, "index.ts"),
|
|
276
|
+
generateTypesFileContent(exportStatements),
|
|
277
|
+
)
|
|
272
278
|
}
|
|
273
279
|
|
|
274
280
|
// Helper function to generate the plugins index.ts
|
|
@@ -302,7 +308,8 @@ const generatePluginTypesIndex = async (sharedDir: string) => {
|
|
|
302
308
|
|
|
303
309
|
// Helper function to generate the main types/index.ts
|
|
304
310
|
const generateMainTypesIndex = async (sharedDir: string) => {
|
|
305
|
-
const content =
|
|
311
|
+
const content =
|
|
312
|
+
generateTypesFileContent(`// Import and re-export all core types
|
|
306
313
|
export type * from './core'
|
|
307
314
|
|
|
308
315
|
// Import and re-export all plugin types
|
package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/project.service.ts
CHANGED
|
@@ -150,7 +150,7 @@ export class ProjectService {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
/**
|
|
153
|
-
* Core function to
|
|
153
|
+
* Core function to the main project (used until spaces are coming)
|
|
154
154
|
*/
|
|
155
155
|
async _readMainProject(fastify: PluginFastifyInstance): Promise<ProjectType> {
|
|
156
156
|
try {
|
|
@@ -4,6 +4,11 @@ import fastifyPlugin from "fastify-plugin"
|
|
|
4
4
|
import type { User } from "@shared/types"
|
|
5
5
|
import configService from "@c/config/configService"
|
|
6
6
|
import type { WorkSessionDoc } from "@/corePlugins/raclette__core/backend/datatypes/workSession/workSession.schema"
|
|
7
|
+
import {
|
|
8
|
+
ADMIN_SYSTEM_TAG_QUERY,
|
|
9
|
+
type AdminTaggableUser,
|
|
10
|
+
userHasAdminTag,
|
|
11
|
+
} from "@shared/helper/adminTag"
|
|
7
12
|
import { ForbiddenError } from "@/utils/errors"
|
|
8
13
|
|
|
9
14
|
const WORK_SESSION_COLLECTION = "raclette__core-worksession"
|
|
@@ -87,25 +92,20 @@ export const authenticatedOrWorksession = async (
|
|
|
87
92
|
const { _id } = req.user
|
|
88
93
|
const adminTag = await fastify.database
|
|
89
94
|
.collection("raclette__core-tag")
|
|
90
|
-
.findOne(
|
|
91
|
-
let isAdmin = false
|
|
95
|
+
.findOne(ADMIN_SYSTEM_TAG_QUERY)
|
|
92
96
|
const user = await fastify.database
|
|
93
97
|
.collection("raclette__core-user")
|
|
94
98
|
.findOne({ _id }, { projection: { password: 0, resetKey: 0 } })
|
|
95
|
-
if (user
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
})
|
|
99
|
+
if (!user) {
|
|
100
|
+
fastify.log.error("User not found")
|
|
101
|
+
return res.status(401).send({ message: "User not found" })
|
|
99
102
|
}
|
|
103
|
+
const isAdmin = userHasAdminTag(user as AdminTaggableUser, adminTag)
|
|
100
104
|
if (isAdminTagRequired && !isAdmin) {
|
|
101
105
|
fastify.log.error("User not allowed in workbench")
|
|
102
106
|
return res.status(403).send({ message: "User not allowed in workbench" })
|
|
103
107
|
}
|
|
104
108
|
if (isAdmin) user.isAdmin = true
|
|
105
|
-
if (!user) {
|
|
106
|
-
fastify.log.error("User not found")
|
|
107
|
-
return res.status(401).send({ message: "User not found" })
|
|
108
|
-
}
|
|
109
109
|
const account = await fastify.database
|
|
110
110
|
.collection("raclette__core-account")
|
|
111
111
|
.findOne({ _id: user.account })
|
|
@@ -244,24 +244,21 @@ export const authenticate = async (
|
|
|
244
244
|
const { _id } = req.user
|
|
245
245
|
const adminTag = await fastify.database
|
|
246
246
|
.collection("raclette__core-tag")
|
|
247
|
-
.findOne(
|
|
247
|
+
.findOne(ADMIN_SYSTEM_TAG_QUERY)
|
|
248
248
|
// IMPORTANT
|
|
249
249
|
// due to some very weird behaviour, mongoose always returns empty results
|
|
250
250
|
// so we are using mongodb directly until we fix this
|
|
251
|
-
let isAdmin = false
|
|
252
251
|
const user = await fastify.database
|
|
253
252
|
.collection("raclette__core-user")
|
|
254
253
|
.findOne(
|
|
255
254
|
{ _id },
|
|
256
255
|
{ projection: { password: 0, resetKey: 0 } }, // Exclude sensitive fields
|
|
257
256
|
)
|
|
258
|
-
if (user
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
isAdmin = true
|
|
262
|
-
}
|
|
263
|
-
})
|
|
257
|
+
if (!user) {
|
|
258
|
+
fastify.log.error("User not found")
|
|
259
|
+
throw new Error("User not found")
|
|
264
260
|
}
|
|
261
|
+
const isAdmin = userHasAdminTag(user as AdminTaggableUser, adminTag)
|
|
265
262
|
if (isAdminTagRequired && !isAdmin) {
|
|
266
263
|
fastify.log.error("User not allowed in workbench")
|
|
267
264
|
throw new Error("User not allowed in workbench")
|
|
@@ -270,11 +267,6 @@ export const authenticate = async (
|
|
|
270
267
|
user.isAdmin = true
|
|
271
268
|
}
|
|
272
269
|
|
|
273
|
-
if (!user) {
|
|
274
|
-
fastify.log.error("User not found")
|
|
275
|
-
throw new Error("User not found")
|
|
276
|
-
}
|
|
277
|
-
|
|
278
270
|
const account = await fastify.database
|
|
279
271
|
.collection("raclette__core-account")
|
|
280
272
|
.findOne({ _id: user.account })
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const ADMIN_SYSTEM_TAG_QUERY = {
|
|
2
|
+
title: "ADMIN",
|
|
3
|
+
locked: true,
|
|
4
|
+
type: "system",
|
|
5
|
+
} as const
|
|
6
|
+
|
|
7
|
+
export type SystemAdminTagLike = {
|
|
8
|
+
title?: string
|
|
9
|
+
locked?: boolean
|
|
10
|
+
type?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type AdminTaggableUser = {
|
|
14
|
+
tags?: string[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isSystemAdminTag(
|
|
18
|
+
tag: SystemAdminTagLike | null | undefined,
|
|
19
|
+
): boolean {
|
|
20
|
+
if (!tag) return false
|
|
21
|
+
return (
|
|
22
|
+
tag.title === ADMIN_SYSTEM_TAG_QUERY.title &&
|
|
23
|
+
tag.locked === ADMIN_SYSTEM_TAG_QUERY.locked &&
|
|
24
|
+
tag.type === ADMIN_SYSTEM_TAG_QUERY.type
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function userHasAdminTag(
|
|
29
|
+
user: AdminTaggableUser | null | undefined,
|
|
30
|
+
adminTag: { _id: string } | null | undefined,
|
|
31
|
+
): boolean {
|
|
32
|
+
if (!user?.tags?.length || !adminTag?._id) return false
|
|
33
|
+
return user.tags.includes(adminTag._id)
|
|
34
|
+
}
|
|
@@ -1,9 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* ⚠️ WARNING: THIS FILE IS AUTO-GENERATED ⚠️
|
|
3
|
-
*
|
|
4
|
-
* This file is generated by the type generation system.
|
|
5
|
-
* Any manual changes will be overwritten on next server start and should always be committed.
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export type * from './pacifico__openstack-billing-dashboard'
|
|
1
|
+
export type {}
|
|
@@ -19,7 +19,7 @@ export const createRequestParams = (
|
|
|
19
19
|
appRequestCode: req.headers["app-request-code"] ?? "",
|
|
20
20
|
requestId: req.id ?? "",
|
|
21
21
|
type: "",
|
|
22
|
-
broadcast: req.headers["app-broadcast-channels"]
|
|
22
|
+
broadcast: req.headers["app-broadcast-channels"]?.length ? true : false,
|
|
23
23
|
project: projectId,
|
|
24
24
|
userId: req.user?.id,
|
|
25
25
|
options: {},
|