@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
package/CHANGELOG.md CHANGED
@@ -7,15 +7,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [0.1.16] - 05.11.2025 <a href="https://gitlab.com/raclettejs/core/-/compare/v0.1.15...v0.1.16" target="_blank" rel="noopener"><b>Overview of all changes</b></a>
10
+ ## [0.1.18] - 05.12.2025 <a href="https://gitlab.com/raclettejs/core/-/compare/v0.1.17...v0.1.18" target="_blank" rel="noopener"><b>Overview of all changes</b></a>
11
+
12
+ ### Changed
13
+
14
+ - Backend: project data type deletion routes temporarily disabled. Will be reenabled soon
15
+ - Frontend: updated dependencies
16
+
17
+ ### Added
18
+
19
+ - Frontend: usermenu - added alternate render mode for inline composition if no trigger id/title is set
20
+ - Frontend: Appbar can now house interactionLinks and inline compositions
21
+ - Frontend: appbar can now launch compositions in a floating window
22
+ - Backend: added restore endpoints for tag/user/composition/interactionLink
23
+ - Frontend: Token expiry will now redirect automatically to login. Last location during auth loss will be used for redirect later
24
+ - Frontend: Simple search input can now receive custom label and uses debounce before model update
25
+ - Frontend: PageNavigation can now rail
26
+ - Backend: Project dataType can now hold config values
27
+ - Global: Added new Tagtype "event"
28
+
29
+ ### Fixed
30
+
31
+ - Backend: isDeleted handling in getAll routes. isDeleted needs to be explicitly true to see deleted entries
32
+ - Backend: TTL for auth token is not derived from raclette.config
33
+
34
+ ## [0.1.17] - 06.11.2025 <a href="https://gitlab.com/raclettejs/core/-/compare/v0.1.15...v0.1.17" target="_blank" rel="noopener"><b>Overview of all changes</b></a>
11
35
 
12
36
  ### Fixed
13
37
 
14
38
  - Frontend: axios Delete handling
39
+ - Frontend: retrieval of deleted compositions/interactionLinks during socket join
40
+ - Frontend: notifications for delete events now show name
15
41
 
16
42
  ### Added
17
43
 
18
44
  - Frontend: added i18n keys
45
+ - Frontend: hard delete of core datatypes
19
46
  - Backend: getAll routes for core datatypes do now respect set filters
20
47
 
21
48
  ## [0.1.15] - 02.11.2025 <a href="https://gitlab.com/raclettejs/core/-/compare/v0.1.13...v0.1.15" target="_blank" rel="noopener"><b>Overview of all changes</b></a>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raclettejs/core",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "racletteJS core package",
5
5
  "repository": "https://gitlab.com/raclettejs/core",
6
6
  "author": "Pacifico Digital Explorations GmbH <info@raclettejs.com>",
@@ -69,7 +69,7 @@
69
69
  },
70
70
  "devDependencies": {
71
71
  "@eslint/js": "9.35.0",
72
- "@raclettejs/types": "0.1.16",
72
+ "@raclettejs/types": "^0.1.18",
73
73
  "@types/fs-extra": "11.0.4",
74
74
  "@types/js-yaml": "4.0.9",
75
75
  "@types/node": "24.5.1",
@@ -30,7 +30,7 @@
30
30
  "@fastify/sensible": "6.0.3",
31
31
  "@sinclair/typebox": "0.34.41",
32
32
  "@types/vary": "1.1.3",
33
- "axios": "1.12.2",
33
+ "axios": "1.13.2",
34
34
  "bcrypt": "6.0.0",
35
35
  "copyfiles": "2.4.1",
36
36
  "fastify": "5.6.0",
@@ -62,4 +62,4 @@
62
62
  "typescript": "5.9.2",
63
63
  "typescript-eslint": "8.44.0"
64
64
  }
65
- }
65
+ }
@@ -122,7 +122,7 @@ const setupTaskManager = (fastify: FastifyInstance) => {
122
122
 
123
123
  if (removed) {
124
124
  taskMetadata.delete(name)
125
- fastify.log.info(`Cancelled task: ${name}`)
125
+ fastify.log.info(`Canceled task: ${name}`)
126
126
  } else {
127
127
  fastify.log.warn(`Task not found: ${name}`)
128
128
  }
@@ -13,6 +13,7 @@ type RacletteBackendConfig = {
13
13
  plugins?: string[]
14
14
  global?: {
15
15
  requireAuthentication?: boolean
16
+ authTokenExpiryDays?: number
16
17
  }
17
18
  additionalPluginPaths?: string[]
18
19
  }
@@ -130,6 +131,9 @@ export class ConfigService {
130
131
  public isAuthRequired(): boolean {
131
132
  return this.config?.global?.requireAuthentication ?? true
132
133
  }
134
+ public getAuthTokenExpiryDays(): number {
135
+ return this.config?.global?.authTokenExpiryDays ?? 30
136
+ }
133
137
  public isAdminTagRequired(): boolean {
134
138
  return this.config?.onlyAllowLoginWithAdminTag ?? false
135
139
  }
@@ -93,6 +93,7 @@ const registerSocketEvents = (fastify: FastifyInstance): void => {
93
93
  "composition",
94
94
  "readAll",
95
95
  fastify,
96
+ { isDeleted: false },
96
97
  )
97
98
  }
98
99
 
@@ -103,6 +104,7 @@ const registerSocketEvents = (fastify: FastifyInstance): void => {
103
104
  "interactionLink",
104
105
  "readAll",
105
106
  fastify,
107
+ { isDeleted: false },
106
108
  )
107
109
  }
108
110
 
@@ -6,7 +6,7 @@ import type {
6
6
  import type { PluginFastifyInstance } from "types"
7
7
  import { v4 as uuidv4, validate } from "uuid"
8
8
  import { createAccountPayload } from "./helpers/payload"
9
- import { Model } from "mongoose"
9
+ import type { Model } from "mongoose"
10
10
 
11
11
  export type AccountBody = Partial<Omit<AccountType, "_id">> & {
12
12
  _id?: string
@@ -277,9 +277,13 @@ export class AccountService {
277
277
  )
278
278
  }
279
279
 
280
- await this.accountModel.deleteOne({ _id: id })
280
+ const res = await account.deleteOne()
281
281
 
282
- return account.toObject ? account.toObject() : account
282
+ if (res?.deletedCount === 1) {
283
+ return account.toObject ? account.toObject() : account
284
+ }
285
+
286
+ throw new Error(`Deletion of Account with ID ${id} was not successful.`)
283
287
  } catch (error: any) {
284
288
  fastify.log.error(error.message)
285
289
  throw error
@@ -2,6 +2,7 @@ import type { Static } from "@sinclair/typebox"
2
2
  import type { FastifyReply, FastifyRequest } from "fastify"
3
3
  import type { 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(),
@@ -17,19 +18,23 @@ export default (fastify: PluginFastifyInstance) => {
17
18
  }>,
18
19
  reply: FastifyReply,
19
20
  ) => {
20
- try {
21
- const { _id } = req.params
21
+ if (req.user.isAdmin) {
22
+ try {
23
+ const { _id } = req.params
22
24
 
23
- const payload = await accountService.hardDeleteAccount(
24
- fastify,
25
- req.requestParams,
26
- _id,
27
- )
25
+ const payload = await accountService.hardDeleteAccount(
26
+ fastify,
27
+ req.requestParams,
28
+ _id,
29
+ )
28
30
 
29
- return payload
30
- } catch (error: any) {
31
- fastify.log.error(error.message)
32
- return reply.internalServerError(error.message)
31
+ return payload
32
+ } catch (error: any) {
33
+ fastify.log.error(error.message)
34
+ return reply.internalServerError(error.message)
35
+ }
36
+ } else {
37
+ throw new ForbiddenError("Only admins can hard delete this core datatype")
33
38
  }
34
39
  }
35
40
 
@@ -97,6 +97,8 @@ export class CompositionService {
97
97
  filters: Record<string, any> = {},
98
98
  ): Promise<CompositionType[]> {
99
99
  try {
100
+ if (filters.isDeleted !== true && filters.isDeleted !== "true")
101
+ filters.isDeleted = false
100
102
  return await this.compositionModel
101
103
  .find({
102
104
  ...filters,
@@ -292,7 +294,7 @@ export class CompositionService {
292
294
  ): Promise<CompositionType> {
293
295
  try {
294
296
  const composition = await this.compositionModel.findOne({
295
- id,
297
+ _id: id,
296
298
  isDeleted: true,
297
299
  })
298
300
 
@@ -302,9 +304,15 @@ export class CompositionService {
302
304
  )
303
305
  }
304
306
 
305
- await this.compositionModel.deleteOne({ _id: id })
307
+ const res = await composition.deleteOne()
306
308
 
307
- return composition.toObject ? composition.toObject() : composition
309
+ if (res?.deletedCount === 1) {
310
+ return composition.toObject ? composition.toObject() : composition
311
+ }
312
+
313
+ throw new Error(
314
+ `Deletion of Composition with ID ${id} was not successful.`,
315
+ )
308
316
  } catch (error: any) {
309
317
  fastify.log.error(error.message)
310
318
  throw error
@@ -5,11 +5,13 @@ import hardDelete from "./route.composition.hardDelete"
5
5
  import patch from "./route.composition.patch"
6
6
  import post from "./route.composition.post"
7
7
  import remove from "./route.composition.remove"
8
+ import restore from "./route.composition.restore"
8
9
 
9
10
  const registerRoutes = async (fastify: PluginFastifyInstance) => {
10
11
  await fastify.get("/composition/all", getAll(fastify))
11
12
  await fastify.get("/composition/:_id", get(fastify))
12
13
  await fastify.patch("/composition/:_id", patch(fastify))
14
+ await fastify.patch("/composition/:_id/restore", restore(fastify))
13
15
  await fastify.post("/composition", post(fastify))
14
16
  await fastify.delete("/composition/:_id", remove(fastify))
15
17
  await fastify.delete("/composition/:_id/hard", hardDelete(fastify))
@@ -2,6 +2,7 @@ import type { Static } from "@sinclair/typebox"
2
2
  import type { FastifyReply, FastifyRequest } from "fastify"
3
3
  import type { 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(),
@@ -17,19 +18,23 @@ export default (fastify: PluginFastifyInstance) => {
17
18
  }>,
18
19
  reply: FastifyReply,
19
20
  ) => {
20
- try {
21
- const { _id } = req.params
21
+ if (req.user.isAdmin) {
22
+ try {
23
+ const { _id } = req.params
22
24
 
23
- const payload = await compositionService.hardDeleteComposition(
24
- fastify,
25
- req.requestParams,
26
- _id,
27
- )
25
+ const payload = await compositionService.hardDeleteComposition(
26
+ fastify,
27
+ req.requestParams,
28
+ _id,
29
+ )
28
30
 
29
- return payload
30
- } catch (error: any) {
31
- fastify.log.error(error.message)
32
- return reply.internalServerError(error.message)
31
+ return payload
32
+ } catch (error: any) {
33
+ fastify.log.error(error.message)
34
+ return reply.internalServerError(error.message)
35
+ }
36
+ } else {
37
+ throw new ForbiddenError("Only admins can hard delete this core datatype")
33
38
  }
34
39
  }
35
40
 
@@ -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 compositionService = fastify.custom.compositionService
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 compositionService.updateComposition(
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: ["compositionRestored"],
47
+ },
48
+ schema: {
49
+ summary: "restore a composition by id",
50
+ description: "Restore a composition",
51
+ tags: ["core/composition"],
52
+ params: ParamsSchema,
53
+ },
54
+ }
55
+ }
@@ -5,8 +5,8 @@ import type {
5
5
  } from "@c/payload/payloadTypes"
6
6
  import { v4 as uuidv4, validate } from "uuid"
7
7
  import { createInteractionLinkPayload } from "./helpers/payload"
8
- import { PluginFastifyInstance } from "types"
9
- import { Model } from "mongoose"
8
+ import type { PluginFastifyInstance } from "types"
9
+ import type { Model } from "mongoose"
10
10
 
11
11
  export type InteractionLinkBody = Partial<Omit<InteractionLinkType, "_id">> & {
12
12
  _id?: string
@@ -100,6 +100,8 @@ export class InteractionLinkService {
100
100
  filters: Record<string, any> = {},
101
101
  ): Promise<InteractionLinkType[]> {
102
102
  try {
103
+ if (filters.isDeleted !== true && filters.isDeleted !== "true")
104
+ filters.isDeleted = false
103
105
  return await this.interactionLinkModel
104
106
  .find({
105
107
  ...filters,
@@ -352,11 +354,17 @@ export class InteractionLinkService {
352
354
  )
353
355
  }
354
356
 
355
- await this.interactionLinkModel.deleteOne({ id })
357
+ const res = await interactionLink.deleteOne()
356
358
 
357
- return interactionLink.toObject
358
- ? interactionLink.toObject()
359
- : interactionLink
359
+ if (res?.deletedCount === 1) {
360
+ return interactionLink.toObject
361
+ ? interactionLink.toObject()
362
+ : interactionLink
363
+ }
364
+
365
+ throw new Error(
366
+ `Deletion of InteractionLink with ID ${id} was not successful.`,
367
+ )
360
368
  } catch (error: any) {
361
369
  fastify.log.error(error.message)
362
370
  throw error
@@ -5,11 +5,13 @@ import hardDelete from "./route.interactionLink.hardDelete"
5
5
  import patch from "./route.interactionLink.patch"
6
6
  import post from "./route.interactionLink.post"
7
7
  import remove from "./route.interactionLink.remove"
8
+ import restore from "./route.interactionLink.restore"
8
9
 
9
10
  const registerRoutes = async (fastify: PluginFastifyInstance) => {
10
11
  await fastify.get("/interactionLink/all", getAll(fastify))
11
12
  await fastify.get("/interactionLink/:_id", get(fastify))
12
13
  await fastify.patch("/interactionLink/:_id", patch(fastify))
14
+ await fastify.patch("/interactionLink/:_id/restore", restore(fastify))
13
15
  await fastify.post("/interactionLink", post(fastify))
14
16
  await fastify.delete("/interactionLink/:_id", remove(fastify))
15
17
  await fastify.delete("/interactionLink/:_id/hard", hardDelete(fastify))
@@ -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(),
@@ -18,15 +19,21 @@ export default (fastify: PluginFastifyInstance) => {
18
19
  reply: FastifyReply,
19
20
  ) => {
20
21
  try {
21
- const { _id } = req.params
22
+ if (req.user.isAdmin) {
23
+ const { _id } = req.params
22
24
 
23
- const payload = await interactionLinkService.hardDeleteInteractionLink(
24
- fastify,
25
- req.requestParams,
26
- _id,
27
- )
25
+ const payload = await interactionLinkService.hardDeleteInteractionLink(
26
+ fastify,
27
+ req.requestParams,
28
+ _id,
29
+ )
28
30
 
29
- return payload
31
+ return payload
32
+ } else {
33
+ throw new ForbiddenError(
34
+ "Only admins can hard delete this core datatype",
35
+ )
36
+ }
30
37
  } catch (error: any) {
31
38
  fastify.log.error(error.message)
32
39
  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 interactionLinkService = fastify.custom.interactionLinkService
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 interactionLinkService.updateInteractionLink(
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: ["interactionLinkRestored"],
47
+ },
48
+ schema: {
49
+ summary: "restore a interactionLink by id",
50
+ description: "Restore a interactionLink",
51
+ tags: ["core/interactionLink"],
52
+ params: ParamsSchema,
53
+ },
54
+ }
55
+ }
@@ -34,6 +34,23 @@ const projectSchema = new Schema(
34
34
 
35
35
  // soft delete
36
36
  isDeleted: { type: Boolean, default: false },
37
+ config: {
38
+ type: {
39
+ tags: {
40
+ type: [Schema.Types.String],
41
+ ref: "raclette__core-tag",
42
+ default: [],
43
+ },
44
+ railPageNavigation: {
45
+ type: Boolean,
46
+ default: false,
47
+ },
48
+ },
49
+ default: () => ({
50
+ tags: [],
51
+ railPageNavigation: false,
52
+ }),
53
+ },
37
54
  },
38
55
  { timestamps: true },
39
56
  )
@@ -2,16 +2,28 @@ import type { Static } from "@sinclair/typebox"
2
2
  import type { Document } from "mongoose"
3
3
  import { Type } from "@sinclair/typebox"
4
4
  import { PluginFastifyInstance } from "types"
5
-
6
5
  /**
7
6
  * Base Project Schema - Fields common to all operations
8
7
  */
8
+ const projectConfigSchema = Type.Optional(
9
+ Type.Object(
10
+ {
11
+ tags: Type.Array(Type.String(), { default: [] }),
12
+ railPageNavigation: Type.Boolean({ default: false }),
13
+ },
14
+ {
15
+ $id: "#project/config",
16
+ title: "core/project-config",
17
+ },
18
+ ),
19
+ )
9
20
  const baseProjectSchema = {
10
21
  title: Type.Optional(Type.String({ maxLength: 200 })),
11
22
  account: Type.Optional(Type.String({ default: null })),
12
23
  tags: Type.Optional(Type.Array(Type.String(), { default: [] })),
13
24
  description: Type.Optional(Type.String()),
14
25
  isDefault: Type.Optional(Type.Boolean({ default: false })),
26
+ config: projectConfigSchema,
15
27
  }
16
28
 
17
29
  /**
@@ -51,11 +63,7 @@ export const projectCreateSchema = Type.Object(
51
63
  export const projectUpdateSchema = Type.Object(
52
64
  {
53
65
  _id: Type.Optional(Type.String()),
54
- title: Type.Optional(Type.String({ maxLength: 200 })),
55
- account: Type.Optional(Type.String()),
56
- tags: Type.Optional(Type.Array(Type.String())),
57
- description: Type.Optional(Type.String()),
58
- isDefault: Type.Optional(Type.Boolean()),
66
+ ...baseProjectSchema,
59
67
  },
60
68
  {
61
69
  $id: "#project/update",
@@ -104,4 +112,8 @@ export const registerSchemas = (fastify: PluginFastifyInstance) => {
104
112
  schema: projectUpdateSchema,
105
113
  name: "ProjectUpdate",
106
114
  })
115
+ fastify.registerSchema({
116
+ schema: projectConfigSchema,
117
+ name: "ProjectConfig",
118
+ })
107
119
  }
@@ -10,8 +10,8 @@ import type {
10
10
  import type { QueryOptions } from "@m/database/providers/mongodb/service"
11
11
  import { v4 as uuidv4, validate } from "uuid"
12
12
  import { createProjectPayload } from "./helpers/payload"
13
- import { Model } from "mongoose"
14
- import { PluginFastifyInstance } from "types"
13
+ import type { Model } from "mongoose"
14
+ import type { PluginFastifyInstance } from "types"
15
15
 
16
16
  export type ProjectBody = Partial<Omit<ProjectType, "_id">> & { _id?: string }
17
17
 
@@ -316,9 +316,13 @@ export class ProjectService {
316
316
  )
317
317
  }
318
318
 
319
- await this.projectModel.deleteOne({ _id: id })
319
+ const res = await project.deleteOne()
320
320
 
321
- return project.toObject ? project.toObject() : project
321
+ if (res?.deletedCount === 1) {
322
+ return project.toObject ? project.toObject() : project
323
+ }
324
+
325
+ throw new Error(`Deletion of Project with ID ${id} was not successful.`)
322
326
  } catch (error: any) {
323
327
  fastify.log.error(error.message)
324
328
  throw error
@@ -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
@@ -5,11 +5,13 @@ import hardDelete from "./route.tag.hardDelete"
5
5
  import patch from "./route.tag.patch"
6
6
  import post from "./route.tag.post"
7
7
  import remove from "./route.tag.remove"
8
+ import restore from "./route.tag.restore"
8
9
 
9
10
  const registerRoutes = async (fastify: PluginFastifyInstance) => {
10
11
  await fastify.get("/tag/all", getAll(fastify))
11
12
  await fastify.get("/tag/:_id", get(fastify))
12
13
  await fastify.patch("/tag/:_id", patch(fastify))
14
+ await fastify.patch("/tag/:_id/restore", restore(fastify))
13
15
  await fastify.post("/tag/:project/:_id", post(fastify))
14
16
  await fastify.delete("/tag/:_id", remove(fastify))
15
17
  await fastify.delete("/tag/:_id/hard", hardDelete(fastify))