@raclettejs/core 0.1.16 → 0.1.18

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.
Files changed (55) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/package.json +2 -2
  3. package/services/backend/package.json +2 -2
  4. package/services/backend/src/core/backgroundTasks/taskManager.ts +1 -1
  5. package/services/backend/src/core/config/configService.ts +4 -0
  6. package/services/backend/src/core/sockets/socketManager.ts +2 -0
  7. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/account.service.ts +7 -3
  8. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.hardDelete.ts +16 -11
  9. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts +11 -3
  10. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/index.ts +2 -0
  11. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.hardDelete.ts +16 -11
  12. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.restore.ts +55 -0
  13. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/interactionLink.service.ts +14 -6
  14. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/index.ts +2 -0
  15. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.hardDelete.ts +14 -7
  16. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.restore.ts +55 -0
  17. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/project.model.ts +17 -0
  18. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/project.schema.ts +18 -6
  19. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/project.service.ts +8 -4
  20. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/index.ts +5 -5
  21. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/index.ts +2 -0
  22. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.hardDelete.ts +14 -7
  23. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.restore.ts +55 -0
  24. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/tag.service.ts +12 -3
  25. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/index.ts +4 -0
  26. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.hardDelete.ts +55 -0
  27. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.restore.ts +55 -0
  28. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/user.service.ts +33 -0
  29. package/services/backend/src/domains/system/routes/route.user.login.ts +4 -2
  30. package/services/backend/src/shared/types/core/ProjectUpdate.types.ts +1 -0
  31. package/services/backend/src/shared/types/dataTypes/tags.ts +1 -0
  32. package/services/frontend/package.json +8 -8
  33. package/services/frontend/src/core/lib/configService.ts +1 -1
  34. package/services/frontend/src/core/lib/data/fetchDataHandler.ts +7 -19
  35. package/services/frontend/src/core/lib/data/responseTypeHandler.ts +1 -1
  36. package/services/frontend/src/core/lib/data/writeDataHandler.ts +8 -13
  37. package/services/frontend/src/core/lib/dataHelper.ts +15 -0
  38. package/services/frontend/src/core/lib/eventWhitelist.ts +4 -13
  39. package/services/frontend/src/core/lib/userNotifier.ts +2 -1
  40. package/services/frontend/src/core/main.ts +7 -1
  41. package/services/frontend/src/orchestrator/LoginApp.vue +0 -1
  42. package/services/frontend/src/orchestrator/ProductOrchestrator.vue +40 -5
  43. package/services/frontend/src/orchestrator/components/composition/CompositionOverlay.vue +7 -3
  44. package/services/frontend/src/orchestrator/components/composition/WidgetsLayoutLoader.vue +12 -24
  45. package/services/frontend/src/orchestrator/components/input/SimpleSearchInput.vue +16 -4
  46. package/services/frontend/src/orchestrator/components/menu/AppBarMenu.vue +184 -0
  47. package/services/frontend/src/orchestrator/components/menu/UserMenu.vue +62 -46
  48. package/services/frontend/src/orchestrator/components/navigation/PageNavigationSidebar.vue +11 -1
  49. package/services/frontend/src/orchestrator/composables/usePageNavigation.ts +10 -1
  50. package/services/frontend/src/orchestrator/i18n/de-DE.json +1 -0
  51. package/services/frontend/src/orchestrator/i18n/en-EU.json +1 -0
  52. package/services/frontend/src/orchestrator/i18n/sk.json +1 -0
  53. package/services/frontend/src/orchestrator/router/index.ts +16 -0
  54. package/yarn.lock +4 -4
  55. package/services/frontend/src/orchestrator/setup/loginApp.ts +0 -27
@@ -2,6 +2,7 @@ import type { Static } from "@sinclair/typebox"
2
2
  import type { FastifyReply, FastifyRequest } from "fastify"
3
3
  import { PluginFastifyInstance } from "types"
4
4
  import { Type } from "@sinclair/typebox"
5
+ import { ForbiddenError } from "@/utils/errors"
5
6
 
6
7
  const ParamsSchema = Type.Object({
7
8
  _id: Type.String(),
@@ -16,15 +17,21 @@ export default (fastify: PluginFastifyInstance) => {
16
17
  reply: FastifyReply,
17
18
  ) => {
18
19
  try {
19
- const { _id } = req.params
20
+ if (req.user.isAdmin) {
21
+ const { _id } = req.params
20
22
 
21
- const payloadWithTag = await tagService.hardDeleteTag(
22
- fastify,
23
- req.requestParams,
24
- _id,
25
- )
23
+ const payloadWithTag = await tagService.hardDeleteTag(
24
+ fastify,
25
+ req.requestParams,
26
+ _id,
27
+ )
26
28
 
27
- return payloadWithTag
29
+ return payloadWithTag
30
+ } else {
31
+ throw new ForbiddenError(
32
+ "Only admins can hard delete this core datatype",
33
+ )
34
+ }
28
35
  } catch (error: any) {
29
36
  fastify.log.error(error.message)
30
37
  return reply.internalServerError(error.message)
@@ -0,0 +1,55 @@
1
+ import type { Static } from "@sinclair/typebox"
2
+ import type { FastifyReply, FastifyRequest } from "fastify"
3
+ import { PluginFastifyInstance } from "types"
4
+ import { Type } from "@sinclair/typebox"
5
+ import { ForbiddenError } from "@/utils/errors"
6
+
7
+ const ParamsSchema = Type.Object({
8
+ _id: Type.String(),
9
+ })
10
+ type Params = Static<typeof ParamsSchema>
11
+
12
+ export default (fastify: PluginFastifyInstance) => {
13
+ const tagService = fastify.custom.tagService
14
+
15
+ const handler = async (
16
+ req: FastifyRequest<{ Params: Params }>,
17
+ reply: FastifyReply,
18
+ ) => {
19
+ try {
20
+ if (req.user.isAdmin) {
21
+ const { _id } = req.params
22
+
23
+ const payload = await tagService.updateTag(
24
+ fastify,
25
+ req.requestParams,
26
+ _id,
27
+ { isDeleted: false },
28
+ req.user._id,
29
+ )
30
+
31
+ return payload
32
+ } else {
33
+ throw new ForbiddenError("Only admins can restore this core datatype")
34
+ }
35
+ } catch (error: any) {
36
+ fastify.log.error(error.message)
37
+ return reply.internalServerError(error.message)
38
+ }
39
+ }
40
+
41
+ return {
42
+ handler,
43
+ onRequest: [fastify.authenticate],
44
+ config: {
45
+ type: "dataRestore",
46
+ broadcastChannels: ["tagRestored"],
47
+ },
48
+ schema: {
49
+ summary: "restore a tag by id",
50
+ description: "Restore a tag",
51
+ tags: ["core/tag"],
52
+ params: ParamsSchema,
53
+ },
54
+ }
55
+ }
@@ -131,6 +131,8 @@ export class TagService {
131
131
  options: QueryOptions = {},
132
132
  ): Promise<TagType[]> {
133
133
  try {
134
+ if (filter.isDeleted !== true && filter.isDeleted !== "true")
135
+ filter.isDeleted = false
134
136
  // Start building the query
135
137
  let query = this.tagModel.find(filter)
136
138
 
@@ -318,7 +320,10 @@ export class TagService {
318
320
  id: string,
319
321
  ): Promise<TagType> {
320
322
  try {
321
- const tag = await this.tagModel.findOne({ _id: id, isDeleted: true })
323
+ const tag = await this.tagModel.findOne({
324
+ _id: id,
325
+ isDeleted: true,
326
+ })
322
327
 
323
328
  if (!tag) {
324
329
  throw new Error(
@@ -326,9 +331,13 @@ export class TagService {
326
331
  )
327
332
  }
328
333
 
329
- await this.tagModel.deleteOne({ _id: id })
334
+ const res = await tag.deleteOne()
330
335
 
331
- return tag.toObject ? tag.toObject() : tag
336
+ if (res?.deletedCount === 1) {
337
+ return tag.toObject ? tag.toObject() : tag
338
+ }
339
+
340
+ throw new Error(`Deletion of Tag with ID ${id} was not successful.`)
332
341
  } catch (error: any) {
333
342
  fastify.log.error(error.message)
334
343
  throw error
@@ -5,13 +5,17 @@ import patch from "./route.user.patch"
5
5
  import patchLastProject from "./route.user.patchLastProject"
6
6
  import post from "./route.user.post"
7
7
  import remove from "./route.user.remove"
8
+ import hardDelete from "./route.user.hardDelete"
9
+ import restore from "./route.user.restore"
8
10
 
9
11
  const registerRoutes = async (fastify: PluginFastifyInstance) => {
10
12
  await fastify.get("/user/all", getAll(fastify))
11
13
  await fastify.get("/user/:_id", get(fastify))
12
14
  await fastify.patch("/user/:_id", patch(fastify))
15
+ await fastify.patch("/user/:_id/restore", restore(fastify))
13
16
  await fastify.post("/user", post(fastify))
14
17
  await fastify.delete("/user/:_id", remove(fastify))
18
+ await fastify.delete("/user/:_id/hard", hardDelete(fastify))
15
19
  await fastify.patch(
16
20
  "/user/last-project/:projectId",
17
21
  patchLastProject(fastify),
@@ -0,0 +1,55 @@
1
+ import type { Static } from "@sinclair/typebox"
2
+ import type { FastifyReply, FastifyRequest } from "fastify"
3
+ import { PluginFastifyInstance } from "types"
4
+ import { Type } from "@sinclair/typebox"
5
+ import { ForbiddenError } from "@/utils/errors"
6
+
7
+ const ParamsSchema = Type.Object({
8
+ _id: Type.String(),
9
+ })
10
+ type Params = Static<typeof ParamsSchema>
11
+
12
+ export default (fastify: PluginFastifyInstance) => {
13
+ const userService = fastify.custom.userService
14
+
15
+ const handler = async (
16
+ req: FastifyRequest<{ Params: Params }>,
17
+ reply: FastifyReply,
18
+ ) => {
19
+ try {
20
+ if (req.user.isAdmin) {
21
+ const { _id } = req.params
22
+
23
+ const payloadWithUser = await userService.hardDeleteUser(
24
+ fastify,
25
+ req.requestParams,
26
+ _id,
27
+ )
28
+
29
+ return payloadWithUser
30
+ } else {
31
+ throw new ForbiddenError(
32
+ "Only admins can hard delete this core datatype",
33
+ )
34
+ }
35
+ } catch (error: any) {
36
+ fastify.log.error(error.message)
37
+ return reply.internalServerError(error.message)
38
+ }
39
+ }
40
+
41
+ return {
42
+ handler,
43
+ onRequest: [fastify.authenticate],
44
+ config: {
45
+ type: "dataHardDeleted",
46
+ broadcastChannels: ["userHardDeleted"],
47
+ },
48
+ schema: {
49
+ summary: "Permanently delete a user by id",
50
+ description: "Hard delete a user",
51
+ tags: ["core/user"],
52
+ params: ParamsSchema,
53
+ },
54
+ }
55
+ }
@@ -0,0 +1,55 @@
1
+ import type { Static } from "@sinclair/typebox"
2
+ import type { FastifyReply, FastifyRequest } from "fastify"
3
+ import { PluginFastifyInstance } from "types"
4
+ import { Type } from "@sinclair/typebox"
5
+ import { ForbiddenError } from "@/utils/errors"
6
+
7
+ const ParamsSchema = Type.Object({
8
+ _id: Type.String(),
9
+ })
10
+ type Params = Static<typeof ParamsSchema>
11
+
12
+ export default (fastify: PluginFastifyInstance) => {
13
+ const userService = fastify.custom.userService
14
+
15
+ const handler = async (
16
+ req: FastifyRequest<{ Params: Params }>,
17
+ reply: FastifyReply,
18
+ ) => {
19
+ try {
20
+ if (req.user.isAdmin) {
21
+ const { _id } = req.params
22
+
23
+ const payload = await userService.updateUser(
24
+ fastify,
25
+ req.requestParams,
26
+ _id,
27
+ { isDeleted: false },
28
+ req.user._id,
29
+ )
30
+
31
+ return payload
32
+ } else {
33
+ throw new ForbiddenError("Only admins can restore this core datatype")
34
+ }
35
+ } catch (error: any) {
36
+ fastify.log.error(error.message)
37
+ return reply.internalServerError(error.message)
38
+ }
39
+ }
40
+
41
+ return {
42
+ handler,
43
+ onRequest: [fastify.authenticate],
44
+ config: {
45
+ type: "dataRestore",
46
+ broadcastChannels: ["userRestored"],
47
+ },
48
+ schema: {
49
+ summary: "restore a user by id",
50
+ description: "Restore a user",
51
+ tags: ["core/user"],
52
+ params: ParamsSchema,
53
+ },
54
+ }
55
+ }
@@ -135,6 +135,7 @@ export class UserService {
135
135
  options: QueryOptions = {},
136
136
  ): Promise<UserType[]> {
137
137
  try {
138
+ if (filter.isDeleted !== true && filter.isDeleted !=='true') filter.isDeleted = false
138
139
  // Start building the query
139
140
  let query = this.userModel.find(filter)
140
141
 
@@ -287,6 +288,38 @@ export class UserService {
287
288
  return payload
288
289
  }
289
290
 
291
+ /**
292
+ * Core function to hard delete an interaction link (returns raw data)
293
+ */
294
+ async _hardDeleteUser(
295
+ fastify: PluginFastifyInstance,
296
+ id: string,
297
+ ): Promise<UserType> {
298
+ try {
299
+ const user = await this.userModel.findOne({
300
+ _id: id,
301
+ isDeleted: true,
302
+ })
303
+
304
+ if (!user) {
305
+ throw new Error(
306
+ `User with ID ${id} not found or not marked for deletion`,
307
+ )
308
+ }
309
+
310
+ const res = await user.deleteOne()
311
+
312
+ if (res?.deletedCount === 1) {
313
+ return user.toObject ? user.toObject() : user
314
+ }
315
+
316
+ throw new Error(`Deletion of User with ID ${id} was not successful.`)
317
+ } catch (error: any) {
318
+ fastify.log.error(error.message)
319
+ throw error
320
+ }
321
+ }
322
+
290
323
  /**
291
324
  * Core function to update user's last accessed project (returns raw data)
292
325
  */
@@ -1,5 +1,6 @@
1
1
  import type { UserDoc } from "@/corePlugins/raclette__core/backend/datatypes/user/user.schema"
2
2
  import type { Static } from "@sinclair/typebox"
3
+ import configService from "@/core/config/configService"
3
4
  import type { FastifyRequest, FastifyReply, FastifyInstance } from "fastify"
4
5
  import { Type } from "@sinclair/typebox"
5
6
  import { toPublicUser } from "@/corePlugins/raclette__core/backend/datatypes/user/user.schema"
@@ -183,9 +184,10 @@ export default (fastify: FastifyInstance) => {
183
184
  deviceId: deviceId || request.ip,
184
185
  iat: Math.floor(Date.now() / 1000),
185
186
  }
186
-
187
+ // Set the expiry time/TTL for the auth token to the configured value
188
+ const expiryDays = configService.getAuthTokenExpiryDays()
187
189
  const token = fastify.jwt.sign(tokenPayload, {
188
- expiresIn: "24h",
190
+ expiresIn: `${expiryDays}d`,
189
191
  })
190
192
  const decoded = fastify.jwt.decode(token)
191
193
 
@@ -11,6 +11,7 @@ export type ProjectUpdate = {
11
11
  title?: string
12
12
  account?: string
13
13
  tags?: string[]
14
+ config: object
14
15
  description?: string
15
16
  isDefault?: boolean
16
17
  }
@@ -5,6 +5,7 @@ export const frontendTagTypes = [
5
5
  "user",
6
6
  "project",
7
7
  "category",
8
+ "event"
8
9
  ] as const
9
10
 
10
11
  export const tag = {
@@ -21,9 +21,9 @@
21
21
  "@sinclair/typebox": "0.34.41",
22
22
  "@tailwindcss/vite": "4.1.13",
23
23
  "@tsconfig/node20": "20.1.6",
24
- "@vueuse/core": "13.9.0",
25
- "@vueuse/rxjs": "13.9.0",
26
- "axios": "1.12.2",
24
+ "@vueuse/core": "14.1.0",
25
+ "@vueuse/rxjs": "14.1.0",
26
+ "axios": "1.13.2",
27
27
  "date-fns": "4.1.0",
28
28
  "esm": "3.x",
29
29
  "interactjs": "1.10.27",
@@ -35,10 +35,10 @@
35
35
  "socket.io-client": "4.x",
36
36
  "tailwindcss": "4.1.13",
37
37
  "uuid": "13.0.0",
38
- "vue": "3.5.21",
39
- "vue-i18n": "11.1.12",
40
- "vue-router": "4.5.1",
41
- "vuetify": "3.10.2"
38
+ "vue": "3.5.25",
39
+ "vue-i18n": "11.2.2",
40
+ "vue-router": "4.6.3",
41
+ "vuetify": "3.11.2"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "24.5.1",
@@ -58,4 +58,4 @@
58
58
  "vue-eslint-parser": "10.2.0",
59
59
  "vue-tsc": "3.0.7"
60
60
  }
61
- }
61
+ }
@@ -71,7 +71,7 @@ export class ConfigService {
71
71
  return this.config.plugins || []
72
72
  }
73
73
  public getAuthTokenExpiryDays(): number {
74
- return this.config.global?.authTokenExpiryDays ?? 7
74
+ return this.config.global?.authTokenExpiryDays ?? 30
75
75
  }
76
76
  public getAdditonalPluginPaths(): string[] {
77
77
  return this.config.additionalPluginPaths?.map((e) => `${e}`) || []
@@ -2,6 +2,7 @@ import log from "@racletteCore/lib/logger"
2
2
  import {
3
3
  promisifyActionResolve,
4
4
  markDataAsModified,
5
+ handleErrors,
5
6
  } from "@racletteCore/lib/dataHelper"
6
7
  import { $api, $store } from "@racletteCore"
7
8
  import {
@@ -164,11 +165,11 @@ const cacheOperations = {
164
165
  },
165
166
  }
166
167
 
167
- const finishRemoteQuery = (actionIdentifier, result, params, options) => {
168
+ const finishRemoteQuery = (actionIdentifier, result = {}, params, options) => {
168
169
  const { actionId, cacheKey } = actionIdentifier
169
170
  log.api("finishRemoteQuery", { actionId, params, result })
170
171
 
171
- if (!result.data) {
172
+ if (!result?.data) {
172
173
  return
173
174
  }
174
175
  let resultBody = structuredClone(result.data.body)
@@ -203,7 +204,6 @@ const finishRemoteQuery = (actionIdentifier, result, params, options) => {
203
204
 
204
205
  return itemDispatcher(actionIdentifier, resultObj, true)
205
206
  }
206
-
207
207
  // Unified remote data handler for both GET and POST
208
208
  const handleRemoteRequest = async (
209
209
  actionIdentifier,
@@ -237,20 +237,8 @@ const handleRemoteRequest = async (
237
237
  try {
238
238
  const response =
239
239
  method === "GET"
240
- ? await $api.axios.get(target, config).catch((e) => {
241
- $eventbus.emit("ui/addToSnackbar", {
242
- message: e.response.data.message,
243
- color: "error",
244
- })
245
- throw e
246
- })
247
- : await $api.axios.post(target, params, config).catch((e) => {
248
- $eventbus.emit("ui/addToSnackbar", {
249
- message: e.response.data.message,
250
- color: "error",
251
- })
252
- throw e
253
- })
240
+ ? await $api.axios.get(target, config).catch(handleErrors)
241
+ : await $api.axios.post(target, params, config).catch(handleErrors)
254
242
 
255
243
  return {
256
244
  response,
@@ -258,7 +246,7 @@ const handleRemoteRequest = async (
258
246
  }
259
247
  } catch (e) {
260
248
  if (e.name === "AbortError" || e.name === "CanceledError") {
261
- log.api(`${method} request was cancelled for actionId:`, actionIdentifier)
249
+ log.api(`${method} request was canceled for actionId:`, actionIdentifier)
262
250
  throw e
263
251
  }
264
252
 
@@ -442,7 +430,7 @@ const executeRoute = async (actionIdentifier, validTarget, params, options) => {
442
430
 
443
431
  if (error.name === "AbortError" || error.name === "CanceledError") {
444
432
  log.api(
445
- "HTTP request was cancelled for actionId:",
433
+ "HTTP request was canceled for actionId:",
446
434
  actionIdentifier.actionId,
447
435
  )
448
436
  throw error
@@ -162,7 +162,7 @@ const handler = {
162
162
  requestId,
163
163
  data,
164
164
  })
165
- const deleted = data.deleted || []
165
+ const deleted = data.items || []
166
166
  $store.dispatch({ type: "data/hardDelete", payload: deleted })
167
167
  if (notify) {
168
168
  userNotifier.dataDelete(isOwnRequest, isSystemAction, deleted)
@@ -1,6 +1,10 @@
1
1
  import log from "@racletteCore/lib/logger"
2
- import { $api, $store, $eventbus } from "@racletteCore"
3
- import { removeCacheAttributes, capitalize } from "@racletteCore/lib/dataHelper"
2
+ import { $api, $store } from "@racletteCore"
3
+ import {
4
+ removeCacheAttributes,
5
+ capitalize,
6
+ handleErrors,
7
+ } from "@racletteCore/lib/dataHelper"
4
8
  import handleServerItem from "./responseTypeHandler"
5
9
  import { handleOperationTarget } from "@racletteCore/setup/plugin-system"
6
10
 
@@ -25,16 +29,7 @@ const createAxiosConfig = (appRequestCode, pluginItemRequestConfig) => {
25
29
  }
26
30
  return config
27
31
  }
28
- const reportError = (error) => {
29
- const message = error?.response?.data?.message || false
30
32
 
31
- $eventbus.emit("ui/addToSnackbar", {
32
- message,
33
- i18n: message ? false : "core.error.noConnectionToServer",
34
- color: "error",
35
- })
36
- throw error
37
- }
38
33
  const storeActions = {
39
34
  dataMove: (actionId, payload) => {
40
35
  const newParent = payload.item.newParent
@@ -128,7 +123,7 @@ const writeData = async (actionId, operationDefinition, item, options) => {
128
123
  response: res,
129
124
  }
130
125
  })
131
- .catch((e) => reportError(e))
126
+ .catch(handleErrors)
132
127
  } else {
133
128
  return await getAxiosPromise(
134
129
  operationDefinition.method,
@@ -142,7 +137,7 @@ const writeData = async (actionId, operationDefinition, item, options) => {
142
137
  result: handleServerItem(res.data, notify, true),
143
138
  }
144
139
  })
145
- .catch((e) => reportError(e))
140
+ .catch(handleErrors)
146
141
  }
147
142
  }
148
143
 
@@ -7,6 +7,20 @@ import log from "@racletteCore/lib/logger"
7
7
  import { $eventbus } from "@racletteCore/main"
8
8
  import { $store } from "@racletteCore/main"
9
9
 
10
+ const handleErrors = (e) => {
11
+ $eventbus.emit("backend/error", e)
12
+ log.api("handleErrors", e)
13
+ // TODO expand error handling for other transports
14
+ if (e.name === "AxiosError") {
15
+ const errorResponse = e.response || {}
16
+ $eventbus.emit("ui/addToSnackbar", {
17
+ message: errorResponse.data?.message || "error",
18
+ color: "error",
19
+ })
20
+ throw e
21
+ }
22
+ return {}
23
+ }
10
24
  const hasOwnProperty = (obj, prop) =>
11
25
  Object.prototype.hasOwnProperty.call(obj, prop)
12
26
  const fileExists = async (path: string): Promise<boolean> => {
@@ -468,4 +482,5 @@ export {
468
482
  getCookie,
469
483
  hasOwnProperty,
470
484
  fileExists,
485
+ handleErrors,
471
486
  }
@@ -6,19 +6,9 @@
6
6
  * change of state, others represent a request addressed to a specific actor,
7
7
  * e.g. 'asks the http client to get x'
8
8
  *
9
- * @TODO
10
- * - find clear naming policy
11
- * - use speaking event names, even though they are longer
12
- * - distinguish between a information and request (e.g. store_dispatch is a request to the store)
13
9
  */
14
- const legacyEventWhitelist = [
15
- "socket_joinRoom",
16
- "socket_leaveRoom",
17
- "socket_dataUpdated",
18
- "ui_setupProject",
19
- "ui_addToSnackBar",
20
- ]
21
- const eventWhitelist = legacyEventWhitelist.concat([
10
+
11
+ const eventWhitelist = [
22
12
  "forms/init",
23
13
  "forms/validate",
24
14
  "widgets/reset/workingState",
@@ -27,7 +17,8 @@ const eventWhitelist = legacyEventWhitelist.concat([
27
17
  "socket/data/update",
28
18
  "socket/join/room",
29
19
  "socket/leave/room",
30
- ])
20
+ "backend/error",
21
+ ]
31
22
  const eventWildcards = [
32
23
  "query_deleted",
33
24
  "store_actionResolved",
@@ -129,7 +129,7 @@ const userConfigUpdate = (isAnswerToOwnRequest, isSystemAction, items) =>
129
129
  color: "info",
130
130
  })
131
131
 
132
- const dataDelete = (isAnswerToOwnRequest, isSystemAction, deletedItems) =>
132
+ const dataDelete = (isAnswerToOwnRequest, isSystemAction, deletedItems) => {
133
133
  deletedItems.forEach((item) => {
134
134
  if (isAnswerToOwnRequest) {
135
135
  $eventbus.emit("ui/addToSnackbar", {
@@ -153,6 +153,7 @@ const dataDelete = (isAnswerToOwnRequest, isSystemAction, deletedItems) =>
153
153
  })
154
154
  }
155
155
  })
156
+ }
156
157
 
157
158
  const dataMove = (isAnswerToOwnRequest, isSystemAction, items) => {
158
159
  items.forEach((item) => {
@@ -159,12 +159,18 @@ const generateLoginCallback =
159
159
  success: true,
160
160
  token: token,
161
161
  redirect: () => {
162
+ const params = new URLSearchParams(window.location.search)
163
+ const redirect = params.get("redirect") || ""
162
164
  // If we're in the same origin, we keep the history
163
165
  if (
164
166
  _callbackUrl.startsWith(window.location.origin) ||
165
167
  _callbackUrl.startsWith("/")
166
168
  ) {
167
- window.location.href = _callbackUrl
169
+ if (redirect.length && redirect.startsWith("/:")) {
170
+ window.location.href = _callbackUrl + redirect
171
+ } else {
172
+ window.location.reload()
173
+ }
168
174
  } else {
169
175
  // Otherwise do a full page reload to the callback URL
170
176
  window.location.replace(_callbackUrl)
@@ -52,7 +52,6 @@ const onSubmitLogin = async (loginPayload: {
52
52
  }) => {
53
53
  if (props.loginMethod) {
54
54
  const { success, message, redirect } = await props.loginMethod(loginPayload)
55
-
56
55
  if (success) {
57
56
  redirect?.()
58
57
  } else if (message) {