@raclettejs/core 0.1.17 → 0.1.19

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 (49) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/package.json +2 -2
  3. package/services/backend/package.json +2 -2
  4. package/services/backend/src/core/config/configService.ts +4 -0
  5. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts +86 -0
  6. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/index.ts +4 -0
  7. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.post.bulk.ts +58 -0
  8. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.restore.ts +55 -0
  9. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/interactionLink.service.ts +96 -9
  10. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/index.ts +4 -0
  11. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.post.bulk.ts +60 -0
  12. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.restore.ts +55 -0
  13. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/project.model.ts +17 -0
  14. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/project.schema.ts +18 -6
  15. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/index.ts +5 -5
  16. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/index.ts +4 -0
  17. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.post.bulk.ts +58 -0
  18. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.restore.ts +55 -0
  19. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/tag.service.ts +74 -1
  20. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/index.ts +2 -0
  21. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.restore.ts +55 -0
  22. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/user.service.ts +1 -0
  23. package/services/backend/src/domains/system/routes/route.user.login.ts +4 -2
  24. package/services/backend/src/shared/types/core/ProjectUpdate.types.ts +1 -0
  25. package/services/backend/src/shared/types/dataTypes/tags.ts +1 -0
  26. package/services/frontend/package.json +8 -8
  27. package/services/frontend/src/core/lib/configService.ts +1 -1
  28. package/services/frontend/src/core/lib/data/dataApi.ts +10 -1
  29. package/services/frontend/src/core/lib/data/fetchDataHandler.ts +3 -23
  30. package/services/frontend/src/core/lib/data/writeDataHandler.ts +8 -13
  31. package/services/frontend/src/core/lib/dataHelper.ts +15 -0
  32. package/services/frontend/src/core/lib/eggs/snow.ts +32 -43
  33. package/services/frontend/src/core/lib/eventWhitelist.ts +4 -13
  34. package/services/frontend/src/core/main.ts +7 -1
  35. package/services/frontend/src/orchestrator/LoginApp.vue +0 -1
  36. package/services/frontend/src/orchestrator/ProductOrchestrator.vue +40 -5
  37. package/services/frontend/src/orchestrator/components/composition/CompositionOverlay.vue +7 -3
  38. package/services/frontend/src/orchestrator/components/composition/WidgetsLayoutLoader.vue +12 -24
  39. package/services/frontend/src/orchestrator/components/input/SimpleSearchInput.vue +16 -4
  40. package/services/frontend/src/orchestrator/components/menu/AppBarMenu.vue +184 -0
  41. package/services/frontend/src/orchestrator/components/menu/UserMenu.vue +62 -46
  42. package/services/frontend/src/orchestrator/components/navigation/PageNavigationSidebar.vue +11 -1
  43. package/services/frontend/src/orchestrator/composables/usePageNavigation.ts +10 -1
  44. package/services/frontend/src/orchestrator/i18n/de-DE.json +15 -0
  45. package/services/frontend/src/orchestrator/i18n/en-EU.json +15 -0
  46. package/services/frontend/src/orchestrator/i18n/sk.json +15 -0
  47. package/services/frontend/src/orchestrator/router/index.ts +16 -0
  48. package/yarn.lock +4 -4
  49. package/services/frontend/src/orchestrator/setup/loginApp.ts +0 -27
@@ -1,10 +1,10 @@
1
1
  import { PluginFastifyInstance } from "types"
2
2
  import get from "./route.project.get"
3
3
  import getAll from "./route.project.getAll"
4
- import hardDelete from "./route.project.hardDelete"
5
4
  import patch from "./route.project.patch"
6
5
  import post from "./route.project.post"
7
- import remove from "./route.project.remove"
6
+ //import hardDelete from "./route.project.hardDelete"
7
+ //import remove from "./route.project.remove"
8
8
 
9
9
  const registerRoutes = async (fastify: PluginFastifyInstance) => {
10
10
  await fastify.get("/project/all", getAll(fastify))
@@ -12,9 +12,9 @@ const registerRoutes = async (fastify: PluginFastifyInstance) => {
12
12
 
13
13
  await fastify.post("/project", post(fastify))
14
14
  await fastify.patch("/project/:_id", patch(fastify))
15
-
16
- await fastify.delete("/project/:_id", remove(fastify))
17
- await fastify.delete("/project/:_id/hard", hardDelete(fastify))
15
+ // TODO reenable once we have deletion concept and space datatype implemented
16
+ //await fastify.delete("/project/:_id", remove(fastify))
17
+ //await fastify.delete("/project/:_id/hard", hardDelete(fastify))
18
18
  }
19
19
 
20
20
  export default registerRoutes
@@ -4,13 +4,17 @@ import getAll from "./route.tag.getAll"
4
4
  import hardDelete from "./route.tag.hardDelete"
5
5
  import patch from "./route.tag.patch"
6
6
  import post from "./route.tag.post"
7
+ import postBulk from "./route.tag.post.bulk"
7
8
  import remove from "./route.tag.remove"
9
+ import restore from "./route.tag.restore"
8
10
 
9
11
  const registerRoutes = async (fastify: PluginFastifyInstance) => {
10
12
  await fastify.get("/tag/all", getAll(fastify))
11
13
  await fastify.get("/tag/:_id", get(fastify))
12
14
  await fastify.patch("/tag/:_id", patch(fastify))
15
+ await fastify.patch("/tag/:_id/restore", restore(fastify))
13
16
  await fastify.post("/tag/:project/:_id", post(fastify))
17
+ await fastify.post("/tag/bulk", postBulk(fastify))
14
18
  await fastify.delete("/tag/:_id", remove(fastify))
15
19
  await fastify.delete("/tag/:_id/hard", hardDelete(fastify))
16
20
  }
@@ -0,0 +1,58 @@
1
+ import type { TagBody } from "../tag.service"
2
+ import type { FastifyReply, FastifyRequest } from "fastify"
3
+ import type { PluginFastifyInstance } from "types"
4
+ import { tagCreateSchema } from "../tag.schema"
5
+
6
+ export default (fastify: PluginFastifyInstance) => {
7
+ const tagService = fastify.custom.tagService
8
+
9
+ const handler = async (
10
+ req: FastifyRequest<{
11
+ Body: TagBody[]
12
+ }>,
13
+ reply: FastifyReply,
14
+ ) => {
15
+ try {
16
+ // Validate that body is an array
17
+ if (!Array.isArray(req.body)) {
18
+ return reply.badRequest("Request body must be an array of tags")
19
+ }
20
+
21
+ // Validate that array is not empty
22
+ if (req.body.length === 0) {
23
+ return reply.badRequest("Request body cannot be an empty array")
24
+ }
25
+
26
+ const payload = await tagService.createTags(
27
+ fastify,
28
+ req.requestParams,
29
+ req.body,
30
+ req.user._id,
31
+ )
32
+
33
+ return payload
34
+ } catch (error: any) {
35
+ fastify.log.error(error.message)
36
+
37
+ return reply.internalServerError(error.message)
38
+ }
39
+ }
40
+
41
+ return {
42
+ handler,
43
+ onRequest: [fastify.authenticate],
44
+ config: {
45
+ type: "dataCreate",
46
+ broadcastChannels: ["tagsCreated"],
47
+ },
48
+ schema: {
49
+ summary: "Create multiple tags in bulk",
50
+ tags: ["core/tag"],
51
+ body: {
52
+ type: "array",
53
+ items: tagCreateSchema,
54
+ minItems: 1,
55
+ },
56
+ },
57
+ }
58
+ }
@@ -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
+ }
@@ -8,7 +8,9 @@ import type { PluginFastifyInstance } from "types"
8
8
  import { v4 as uuidv4, validate } from "uuid"
9
9
  import { createTagPayload } from "./helpers/payload"
10
10
  import type { Model } from "mongoose"
11
-
11
+ export type TagBody = Partial<Omit<TagType, "_id">> & {
12
+ _id?: string
13
+ }
12
14
  export class TagService {
13
15
  private tagModel: Model<TagType>
14
16
 
@@ -131,6 +133,8 @@ export class TagService {
131
133
  options: QueryOptions = {},
132
134
  ): Promise<TagType[]> {
133
135
  try {
136
+ if (filter.isDeleted !== true && filter.isDeleted !== "true")
137
+ filter.isDeleted = false
134
138
  // Start building the query
135
139
  let query = this.tagModel.find(filter)
136
140
 
@@ -367,6 +371,75 @@ export class TagService {
367
371
 
368
372
  fastify.emit("tagHardDeleted", payload)
369
373
 
374
+ return payload
375
+ }
376
+ /**
377
+ * Core function to create multiple tags (returns raw data)
378
+ */
379
+ async _createTags(
380
+ fastify: PluginFastifyInstance,
381
+ tagBodies: TagBody[],
382
+ userId: string,
383
+ ): Promise<TagType[]> {
384
+ try {
385
+ const tagsToCreate: TagBody[] = []
386
+
387
+ // Validate and prepare all tags
388
+ for (const tagBody of tagBodies) {
389
+ if (tagBody._id) {
390
+ const uuidValid = validate(tagBody._id)
391
+
392
+ if (!uuidValid) {
393
+ throw new Error(`Invalid ID - not a valid uuid v4: ${tagBody._id}`)
394
+ }
395
+
396
+ const duplicate = await this.tagModel.findOne({
397
+ _id: tagBody._id,
398
+ })
399
+
400
+ if (duplicate) {
401
+ throw new Error(
402
+ `An entry with this id already exists: ${tagBody._id}`,
403
+ )
404
+ }
405
+ } else {
406
+ tagBody._id = uuidv4()
407
+ }
408
+
409
+ tagsToCreate.push({
410
+ ...tagBody,
411
+ author: userId,
412
+ lastEditor: userId,
413
+ })
414
+ }
415
+
416
+ // Bulk insert
417
+ const tags = await this.tagModel.insertMany(tagsToCreate)
418
+
419
+ fastify.log.info(`[API] Created ${tags.length} tags in bulk`)
420
+
421
+ return tags.map((comp) => (comp.toObject ? comp.toObject() : comp))
422
+ } catch (error: any) {
423
+ fastify.log.error(error.message)
424
+ throw error
425
+ }
426
+ }
427
+
428
+ /**
429
+ * Create multiple tags with payload wrapping and event emission
430
+ */
431
+ async createTags(
432
+ fastify: PluginFastifyInstance,
433
+ requestData: FrontendPayloadRequestData,
434
+ tagBodies: TagBody[],
435
+ userId: string,
436
+ ): Promise<FrontendPayload<TagType[]>> {
437
+ const tags = await this._createTags(fastify, tagBodies, userId)
438
+
439
+ const payload = await createTagPayload(fastify, tags, requestData)
440
+
441
+ fastify.emit("tagsCreated", payload)
442
+
370
443
  return payload
371
444
  }
372
445
  }
@@ -6,11 +6,13 @@ import patchLastProject from "./route.user.patchLastProject"
6
6
  import post from "./route.user.post"
7
7
  import remove from "./route.user.remove"
8
8
  import hardDelete from "./route.user.hardDelete"
9
+ import restore from "./route.user.restore"
9
10
 
10
11
  const registerRoutes = async (fastify: PluginFastifyInstance) => {
11
12
  await fastify.get("/user/all", getAll(fastify))
12
13
  await fastify.get("/user/:_id", get(fastify))
13
14
  await fastify.patch("/user/:_id", patch(fastify))
15
+ await fastify.patch("/user/:_id/restore", restore(fastify))
14
16
  await fastify.post("/user", post(fastify))
15
17
  await fastify.delete("/user/:_id", remove(fastify))
16
18
  await fastify.delete("/user/:_id/hard", hardDelete(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 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
 
@@ -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}`) || []
@@ -29,7 +29,16 @@ const $writeDataApi = (
29
29
  const _executeParams = clone(executeParams)
30
30
  errorSubject$.next(null)
31
31
  responseSubject$.next({})
32
- const queryParams = mergeDeepRight(_params, _executeParams)
32
+ let queryParams
33
+ if (Array.isArray(_executeParams)) {
34
+ if (Array.isArray(_params)) {
35
+ queryParams = _params.concat(_executeParams)
36
+ }
37
+ queryParams = _executeParams
38
+ } else {
39
+ queryParams = mergeDeepRight(_params, _executeParams)
40
+ }
41
+
33
42
  if (options.responseType === "stream") {
34
43
  return writeData(actionId, operationDefinition, queryParams, options)
35
44
  }
@@ -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 {
@@ -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,28 +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
- if (e.name !== "CanceledError") {
242
- $eventbus.emit("ui/addToSnackbar", {
243
- message: e.response?.data?.message || "error",
244
- color: "error",
245
- })
246
- throw e
247
- }
248
- log.api("axios/get/error", e)
249
- return {}
250
- })
251
- : await $api.axios.post(target, params, config).catch((e) => {
252
- if (e.name !== "CanceledError") {
253
- $eventbus.emit("ui/addToSnackbar", {
254
- message: e.response?.data?.message || "error",
255
- color: "error",
256
- })
257
- throw e
258
- }
259
- log.api("axios/post/error", e)
260
- return {}
261
- })
240
+ ? await $api.axios.get(target, config).catch(handleErrors)
241
+ : await $api.axios.post(target, params, config).catch(handleErrors)
262
242
 
263
243
  return {
264
244
  response,
@@ -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
  }