From 7928cb9c96477b20e77369b2686c6ead870d78e6 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 5 Nov 2025 19:58:16 +0100 Subject: [PATCH 01/11] fiddeling with hardDelete --- CHANGELOG.md | 4 +++- services/backend/src/core/sockets/socketManager.ts | 2 ++ .../backend/datatypes/composition/composition.service.ts | 2 +- .../datatypes/interactionLink/interactionLink.service.ts | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b96de94..e429e56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.1.16] - 05.11.2025 Overview of all changes +## [0.1.17] - 05.11.2025 Overview of all changes ### Fixed - Frontend: axios Delete handling +- Frontend: retrieval of deleted compositions/interactionLinks during socket join +- Frontend: hard delete of interactionLinks ### Added diff --git a/services/backend/src/core/sockets/socketManager.ts b/services/backend/src/core/sockets/socketManager.ts index c108a58..1464b36 100644 --- a/services/backend/src/core/sockets/socketManager.ts +++ b/services/backend/src/core/sockets/socketManager.ts @@ -93,6 +93,7 @@ const registerSocketEvents = (fastify: FastifyInstance): void => { "composition", "readAll", fastify, + { isDeleted: false }, ) } @@ -103,6 +104,7 @@ const registerSocketEvents = (fastify: FastifyInstance): void => { "interactionLink", "readAll", fastify, + { isDeleted: false }, ) } diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts index e9bc301..4314b35 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts @@ -292,7 +292,7 @@ export class CompositionService { ): Promise { try { const composition = await this.compositionModel.findOne({ - id, + _id: id, isDeleted: true, }) diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/interactionLink.service.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/interactionLink.service.ts index 018678c..0b9a740 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/interactionLink.service.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/interactionLink.service.ts @@ -352,7 +352,7 @@ export class InteractionLinkService { ) } - await this.interactionLinkModel.deleteOne({ id }) + await this.interactionLinkModel.deleteOne({ _id: id }) return interactionLink.toObject ? interactionLink.toObject() -- GitLab From 7b852c64ae0057683be82e22e0182bc680a9de9d Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 5 Nov 2025 20:13:45 +0100 Subject: [PATCH 02/11] fiddeling with hardDelete --- services/backend/src/core/payload/payloadTypes.ts | 3 +++ services/backend/src/utils/request.utils.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/services/backend/src/core/payload/payloadTypes.ts b/services/backend/src/core/payload/payloadTypes.ts index e290b70..8264981 100644 --- a/services/backend/src/core/payload/payloadTypes.ts +++ b/services/backend/src/core/payload/payloadTypes.ts @@ -1,3 +1,5 @@ +import { User } from "@shared/types" + export type QueryOptions = { limit?: number offset?: number @@ -15,6 +17,7 @@ export type FrontendPayloadRequestData = { format?: string userId?: string options?: any | QueryOptions + user?: User } export type FrontendPayloadBody = { diff --git a/services/backend/src/utils/request.utils.ts b/services/backend/src/utils/request.utils.ts index 8b8c551..9952eca 100644 --- a/services/backend/src/utils/request.utils.ts +++ b/services/backend/src/utils/request.utils.ts @@ -19,6 +19,7 @@ export const createRequestParams = ( broadcast: req.headers["app-broadcast-channels"] ?? false, project: req.headers["current-project"] ?? "", userId: req.user?.id, + user: req.user, options: {}, ...config, } -- GitLab From 0fa35dcca86f11e54988f7e41fe9ce74664b118d Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 5 Nov 2025 20:19:36 +0100 Subject: [PATCH 03/11] fiddeling with hardDeletes --- services/backend/src/core/payload/payloadTypes.ts | 3 --- services/backend/src/utils/request.utils.ts | 1 - 2 files changed, 4 deletions(-) diff --git a/services/backend/src/core/payload/payloadTypes.ts b/services/backend/src/core/payload/payloadTypes.ts index 8264981..e290b70 100644 --- a/services/backend/src/core/payload/payloadTypes.ts +++ b/services/backend/src/core/payload/payloadTypes.ts @@ -1,5 +1,3 @@ -import { User } from "@shared/types" - export type QueryOptions = { limit?: number offset?: number @@ -17,7 +15,6 @@ export type FrontendPayloadRequestData = { format?: string userId?: string options?: any | QueryOptions - user?: User } export type FrontendPayloadBody = { diff --git a/services/backend/src/utils/request.utils.ts b/services/backend/src/utils/request.utils.ts index 9952eca..8b8c551 100644 --- a/services/backend/src/utils/request.utils.ts +++ b/services/backend/src/utils/request.utils.ts @@ -19,7 +19,6 @@ export const createRequestParams = ( broadcast: req.headers["app-broadcast-channels"] ?? false, project: req.headers["current-project"] ?? "", userId: req.user?.id, - user: req.user, options: {}, ...config, } -- GitLab From 5758e4a8eee97e0b592b92bcbcc1d2af20b408b4 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 6 Nov 2025 09:32:26 +0100 Subject: [PATCH 04/11] fix: request cancellation --- .../src/core/backgroundTasks/taskManager.ts | 2 +- .../src/core/lib/data/fetchDataHandler.ts | 36 +++++++++++-------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/services/backend/src/core/backgroundTasks/taskManager.ts b/services/backend/src/core/backgroundTasks/taskManager.ts index 1a31115..5e438a7 100644 --- a/services/backend/src/core/backgroundTasks/taskManager.ts +++ b/services/backend/src/core/backgroundTasks/taskManager.ts @@ -122,7 +122,7 @@ const setupTaskManager = (fastify: FastifyInstance) => { if (removed) { taskMetadata.delete(name) - fastify.log.info(`Cancelled task: ${name}`) + fastify.log.info(`Canceled task: ${name}`) } else { fastify.log.warn(`Task not found: ${name}`) } diff --git a/services/frontend/src/core/lib/data/fetchDataHandler.ts b/services/frontend/src/core/lib/data/fetchDataHandler.ts index 2775150..27f2715 100644 --- a/services/frontend/src/core/lib/data/fetchDataHandler.ts +++ b/services/frontend/src/core/lib/data/fetchDataHandler.ts @@ -164,11 +164,11 @@ const cacheOperations = { }, } -const finishRemoteQuery = (actionIdentifier, result, params, options) => { +const finishRemoteQuery = (actionIdentifier, result = {}, params, options) => { const { actionId, cacheKey } = actionIdentifier log.api("finishRemoteQuery", { actionId, params, result }) - if (!result.data) { + if (!result?.data) { return } let resultBody = structuredClone(result.data.body) @@ -238,18 +238,26 @@ const handleRemoteRequest = async ( const response = method === "GET" ? await $api.axios.get(target, config).catch((e) => { - $eventbus.emit("ui/addToSnackbar", { - message: e.response.data.message, - color: "error", - }) - throw e + if (e.name !== "CanceledError") { + $eventbus.emit("ui/addToSnackbar", { + message: e.response?.data?.message || "error", + color: "error", + }) + throw e + } + log.api("axios/get/error", e) + return {} }) : await $api.axios.post(target, params, config).catch((e) => { - $eventbus.emit("ui/addToSnackbar", { - message: e.response.data.message, - color: "error", - }) - throw e + if (e.name !== "CanceledError") { + $eventbus.emit("ui/addToSnackbar", { + message: e.response?.data?.message || "error", + color: "error", + }) + throw e + } + log.api("axios/post/error", e) + return {} }) return { @@ -258,7 +266,7 @@ const handleRemoteRequest = async ( } } catch (e) { if (e.name === "AbortError" || e.name === "CanceledError") { - log.api(`${method} request was cancelled for actionId:`, actionIdentifier) + log.api(`${method} request was canceled for actionId:`, actionIdentifier) throw e } @@ -442,7 +450,7 @@ const executeRoute = async (actionIdentifier, validTarget, params, options) => { if (error.name === "AbortError" || error.name === "CanceledError") { log.api( - "HTTP request was cancelled for actionId:", + "HTTP request was canceled for actionId:", actionIdentifier.actionId, ) throw error -- GitLab From f4655251355169451605ca8a2911ab3d99dd05a2 Mon Sep 17 00:00:00 2001 From: Michelle Fuchs Date: Thu, 6 Nov 2025 09:58:29 +0100 Subject: [PATCH 05/11] feat: add correct error log for comp and IL --- .../composition/composition.service.ts | 10 ++++++++-- .../interactionLink/interactionLink.service.ts | 18 ++++++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts index 4314b35..5e36d5c 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts @@ -302,9 +302,15 @@ export class CompositionService { ) } - await this.compositionModel.deleteOne({ _id: id }) + const res = await composition.deleteOne() - return composition.toObject ? composition.toObject() : composition + if (res?.deletedCount === 1) { + return composition.toObject ? composition.toObject() : composition + } + + throw new Error( + `Deletion of Composition with ID ${id} was not successful.`, + ) } catch (error: any) { fastify.log.error(error.message) throw error diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/interactionLink.service.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/interactionLink.service.ts index 0b9a740..ea3a69a 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/interactionLink.service.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/interactionLink.service.ts @@ -5,8 +5,8 @@ import type { } from "@c/payload/payloadTypes" import { v4 as uuidv4, validate } from "uuid" import { createInteractionLinkPayload } from "./helpers/payload" -import { PluginFastifyInstance } from "types" -import { Model } from "mongoose" +import type { PluginFastifyInstance } from "types" +import type { Model } from "mongoose" export type InteractionLinkBody = Partial> & { _id?: string @@ -352,11 +352,17 @@ export class InteractionLinkService { ) } - await this.interactionLinkModel.deleteOne({ _id: id }) + const res = await interactionLink.deleteOne() - return interactionLink.toObject - ? interactionLink.toObject() - : interactionLink + if (res?.deletedCount === 1) { + return interactionLink.toObject + ? interactionLink.toObject() + : interactionLink + } + + throw new Error( + `Deletion of InteractionLink with ID ${id} was not successful.`, + ) } catch (error: any) { fastify.log.error(error.message) throw error -- GitLab From a2bf3921ef79ce31e8bca31f9ad29559601486f4 Mon Sep 17 00:00:00 2001 From: Michelle Fuchs Date: Thu, 6 Nov 2025 10:03:43 +0100 Subject: [PATCH 06/11] fix: code of hardDelete for core datatypes --- .../datatypes/account/account.service.ts | 10 ++++-- .../datatypes/project/project.service.ts | 12 ++++--- .../backend/datatypes/tag/tag.service.ts | 13 ++++++-- .../backend/datatypes/user/user.service.ts | 32 +++++++++++++++++++ 4 files changed, 57 insertions(+), 10 deletions(-) diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/account.service.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/account.service.ts index ad5907a..fde382d 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/account.service.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/account.service.ts @@ -6,7 +6,7 @@ import type { import type { PluginFastifyInstance } from "types" import { v4 as uuidv4, validate } from "uuid" import { createAccountPayload } from "./helpers/payload" -import { Model } from "mongoose" +import type { Model } from "mongoose" export type AccountBody = Partial> & { _id?: string @@ -277,9 +277,13 @@ export class AccountService { ) } - await this.accountModel.deleteOne({ _id: id }) + const res = await account.deleteOne() - return account.toObject ? account.toObject() : account + if (res?.deletedCount === 1) { + return account.toObject ? account.toObject() : account + } + + throw new Error(`Deletion of Account with ID ${id} was not successful.`) } catch (error: any) { fastify.log.error(error.message) throw error diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/project.service.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/project.service.ts index a76b329..a61fdbb 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/project.service.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/project.service.ts @@ -10,8 +10,8 @@ import type { import type { QueryOptions } from "@m/database/providers/mongodb/service" import { v4 as uuidv4, validate } from "uuid" import { createProjectPayload } from "./helpers/payload" -import { Model } from "mongoose" -import { PluginFastifyInstance } from "types" +import type { Model } from "mongoose" +import type { PluginFastifyInstance } from "types" export type ProjectBody = Partial> & { _id?: string } @@ -316,9 +316,13 @@ export class ProjectService { ) } - await this.projectModel.deleteOne({ _id: id }) + const res = await project.deleteOne() - return project.toObject ? project.toObject() : project + if (res?.deletedCount === 1) { + return project.toObject ? project.toObject() : project + } + + throw new Error(`Deletion of Project with ID ${id} was not successful.`) } catch (error: any) { fastify.log.error(error.message) throw error diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/tag.service.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/tag.service.ts index c96a437..2b49833 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/tag.service.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/tag.service.ts @@ -318,7 +318,10 @@ export class TagService { id: string, ): Promise { try { - const tag = await this.tagModel.findOne({ _id: id, isDeleted: true }) + const tag = await this.tagModel.findOne({ + _id: id, + isDeleted: true, + }) if (!tag) { throw new Error( @@ -326,9 +329,13 @@ export class TagService { ) } - await this.tagModel.deleteOne({ _id: id }) + const res = await tag.deleteOne() - return tag.toObject ? tag.toObject() : tag + if (res?.deletedCount === 1) { + return tag.toObject ? tag.toObject() : tag + } + + throw new Error(`Deletion of Tag with ID ${id} was not successful.`) } catch (error: any) { fastify.log.error(error.message) throw error diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/user.service.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/user.service.ts index 6469e73..095002f 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/user.service.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/user.service.ts @@ -287,6 +287,38 @@ export class UserService { return payload } + /** + * Core function to hard delete an interaction link (returns raw data) + */ + async _hardDeleteUser( + fastify: PluginFastifyInstance, + id: string, + ): Promise { + try { + const user = await this.userModel.findOne({ + _id: id, + isDeleted: true, + }) + + if (!user) { + throw new Error( + `User with ID ${id} not found or not marked for deletion`, + ) + } + + const res = await user.deleteOne() + + if (res?.deletedCount === 1) { + return user.toObject ? user.toObject() : user + } + + throw new Error(`Deletion of User with ID ${id} was not successful.`) + } catch (error: any) { + fastify.log.error(error.message) + throw error + } + } + /** * Core function to update user's last accessed project (returns raw data) */ -- GitLab From 5655f673ec7f72cfd677f97b75303faa704983da Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 6 Nov 2025 10:05:11 +0100 Subject: [PATCH 07/11] feat: adding isAdmin to hardDelete --- .../routes/route.account.hardDelete.ts | 27 ++++++---- .../routes/route.composition.hardDelete.ts | 27 ++++++---- .../route.interactionLink.hardDelete.ts | 19 ++++--- .../tag/routes/route.tag.hardDelete.ts | 19 ++++--- .../backend/datatypes/user/routes/index.ts | 2 + .../user/routes/route.user.hardDelete.ts | 53 +++++++++++++++++++ 6 files changed, 111 insertions(+), 36 deletions(-) create mode 100644 services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.hardDelete.ts diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.hardDelete.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.hardDelete.ts index 78fc3aa..298536b 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.hardDelete.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.hardDelete.ts @@ -2,6 +2,7 @@ import type { Static } from "@sinclair/typebox" import type { FastifyReply, FastifyRequest } from "fastify" import type { PluginFastifyInstance } from "types" import { Type } from "@sinclair/typebox" +import { ForbiddenError } from "@/utils/errors" const ParamsSchema = Type.Object({ _id: Type.String(), @@ -17,19 +18,23 @@ export default (fastify: PluginFastifyInstance) => { }>, reply: FastifyReply, ) => { - try { - const { _id } = req.params + if (req.user.isAdmin) { + try { + const { _id } = req.params - const payload = await accountService.hardDeleteAccount( - fastify, - req.requestParams, - _id, - ) + const payload = await accountService.hardDeleteAccount( + fastify, + req.requestParams, + _id, + ) - return payload - } catch (error: any) { - fastify.log.error(error.message) - return reply.internalServerError(error.message) + return payload + } catch (error: any) { + fastify.log.error(error.message) + return reply.internalServerError(error.message) + } + } else { + throw new ForbiddenError("Only Admins can hard Delete") } } diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.hardDelete.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.hardDelete.ts index 89bdb3b..7393b8c 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.hardDelete.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.hardDelete.ts @@ -2,6 +2,7 @@ import type { Static } from "@sinclair/typebox" import type { FastifyReply, FastifyRequest } from "fastify" import type { PluginFastifyInstance } from "types" import { Type } from "@sinclair/typebox" +import { ForbiddenError } from "@/utils/errors" const ParamsSchema = Type.Object({ _id: Type.String(), @@ -17,19 +18,23 @@ export default (fastify: PluginFastifyInstance) => { }>, reply: FastifyReply, ) => { - try { - const { _id } = req.params + if (req.user.isAdmin) { + try { + const { _id } = req.params - const payload = await compositionService.hardDeleteComposition( - fastify, - req.requestParams, - _id, - ) + const payload = await compositionService.hardDeleteComposition( + fastify, + req.requestParams, + _id, + ) - return payload - } catch (error: any) { - fastify.log.error(error.message) - return reply.internalServerError(error.message) + return payload + } catch (error: any) { + fastify.log.error(error.message) + return reply.internalServerError(error.message) + } + } else { + throw new ForbiddenError("Only Admins can hard Delete") } } diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.hardDelete.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.hardDelete.ts index 0b24792..f2c2605 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.hardDelete.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.hardDelete.ts @@ -2,6 +2,7 @@ import type { Static } from "@sinclair/typebox" import type { FastifyReply, FastifyRequest } from "fastify" import { PluginFastifyInstance } from "types" import { Type } from "@sinclair/typebox" +import { ForbiddenError } from "@/utils/errors" const ParamsSchema = Type.Object({ _id: Type.String(), @@ -18,15 +19,19 @@ export default (fastify: PluginFastifyInstance) => { reply: FastifyReply, ) => { try { - const { _id } = req.params + if (req.user.isAdmin) { + const { _id } = req.params - const payload = await interactionLinkService.hardDeleteInteractionLink( - fastify, - req.requestParams, - _id, - ) + const payload = await interactionLinkService.hardDeleteInteractionLink( + fastify, + req.requestParams, + _id, + ) - return payload + return payload + } else { + throw new ForbiddenError("Only Admins can hard Delete") + } } catch (error: any) { fastify.log.error(error.message) return reply.internalServerError(error.message) diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.hardDelete.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.hardDelete.ts index 607bbc0..eaea59c 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.hardDelete.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.hardDelete.ts @@ -2,6 +2,7 @@ import type { Static } from "@sinclair/typebox" import type { FastifyReply, FastifyRequest } from "fastify" import { PluginFastifyInstance } from "types" import { Type } from "@sinclair/typebox" +import { ForbiddenError } from "@/utils/errors" const ParamsSchema = Type.Object({ _id: Type.String(), @@ -16,15 +17,19 @@ export default (fastify: PluginFastifyInstance) => { reply: FastifyReply, ) => { try { - const { _id } = req.params + if (req.user.isAdmin) { + const { _id } = req.params - const payloadWithTag = await tagService.hardDeleteTag( - fastify, - req.requestParams, - _id, - ) + const payloadWithTag = await tagService.hardDeleteTag( + fastify, + req.requestParams, + _id, + ) - return payloadWithTag + return payloadWithTag + } else { + throw new ForbiddenError("Only Admins can hard Delete") + } } catch (error: any) { fastify.log.error(error.message) return reply.internalServerError(error.message) diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/index.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/index.ts index 5488639..27cf7a0 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/index.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/index.ts @@ -5,6 +5,7 @@ import patch from "./route.user.patch" import patchLastProject from "./route.user.patchLastProject" import post from "./route.user.post" import remove from "./route.user.remove" +import hardDelete from "./route.user.hardDelete" const registerRoutes = async (fastify: PluginFastifyInstance) => { await fastify.get("/user/all", getAll(fastify)) @@ -12,6 +13,7 @@ const registerRoutes = async (fastify: PluginFastifyInstance) => { await fastify.patch("/user/:_id", patch(fastify)) await fastify.post("/user", post(fastify)) await fastify.delete("/user/:_id", remove(fastify)) + await fastify.delete("/user/:_id/hard", hardDelete(fastify)) await fastify.patch( "/user/last-project/:projectId", patchLastProject(fastify), diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.hardDelete.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.hardDelete.ts new file mode 100644 index 0000000..42246ad --- /dev/null +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.hardDelete.ts @@ -0,0 +1,53 @@ +import type { Static } from "@sinclair/typebox" +import type { FastifyReply, FastifyRequest } from "fastify" +import { PluginFastifyInstance } from "types" +import { Type } from "@sinclair/typebox" +import { ForbiddenError } from "@/utils/errors" + +const ParamsSchema = Type.Object({ + _id: Type.String(), +}) +type Params = Static + +export default (fastify: PluginFastifyInstance) => { + const userService = fastify.custom.userService + + const handler = async ( + req: FastifyRequest<{ Params: Params }>, + reply: FastifyReply, + ) => { + try { + if (req.user.isAdmin) { + const { _id } = req.params + + const payloadWithUser = await userService.hardDeleteUser( + fastify, + req.requestParams, + _id, + ) + + return payloadWithUser + } else { + throw new ForbiddenError("Only Admins can hard Delete") + } + } catch (error: any) { + fastify.log.error(error.message) + return reply.internalServerError(error.message) + } + } + + return { + handler, + onRequest: [fastify.authenticate], + config: { + type: "dataHardDeleted", + broadcastChannels: ["userHardDeleted"], + }, + schema: { + summary: "Permanently delete a user by id", + description: "Hard delete a user", + tags: ["core/user"], + params: ParamsSchema, + }, + } +} -- GitLab From d68ce042e89dc18a1873511b27a43734a8c25eb3 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 6 Nov 2025 10:14:43 +0100 Subject: [PATCH 08/11] chore: updated changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e429e56..1f076ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,11 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Frontend: axios Delete handling - Frontend: retrieval of deleted compositions/interactionLinks during socket join -- Frontend: hard delete of interactionLinks ### Added - Frontend: added i18n keys +- Frontend: hard delete of core datatypes - Backend: getAll routes for core datatypes do now respect set filters ## [0.1.15] - 02.11.2025 Overview of all changes -- GitLab From 6716e29019b632f7c84b43de19554fe60ba83131 Mon Sep 17 00:00:00 2001 From: Michelle Fuchs Date: Thu, 6 Nov 2025 10:15:22 +0100 Subject: [PATCH 09/11] feat: adjust error message --- .../datatypes/account/routes/route.account.hardDelete.ts | 2 +- .../composition/routes/route.composition.hardDelete.ts | 2 +- .../routes/route.interactionLink.hardDelete.ts | 4 +++- .../backend/datatypes/tag/routes/route.tag.hardDelete.ts | 4 +++- .../backend/datatypes/user/routes/route.user.hardDelete.ts | 4 +++- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.hardDelete.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.hardDelete.ts index 298536b..f518603 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.hardDelete.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.hardDelete.ts @@ -34,7 +34,7 @@ export default (fastify: PluginFastifyInstance) => { return reply.internalServerError(error.message) } } else { - throw new ForbiddenError("Only Admins can hard Delete") + throw new ForbiddenError("Only admins can hard delete this core datatype") } } diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.hardDelete.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.hardDelete.ts index 7393b8c..09a6e2a 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.hardDelete.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.hardDelete.ts @@ -34,7 +34,7 @@ export default (fastify: PluginFastifyInstance) => { return reply.internalServerError(error.message) } } else { - throw new ForbiddenError("Only Admins can hard Delete") + throw new ForbiddenError("Only admins can hard delete this core datatype") } } diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.hardDelete.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.hardDelete.ts index f2c2605..6228ed2 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.hardDelete.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.hardDelete.ts @@ -30,7 +30,9 @@ export default (fastify: PluginFastifyInstance) => { return payload } else { - throw new ForbiddenError("Only Admins can hard Delete") + throw new ForbiddenError( + "Only admins can hard delete this core datatype", + ) } } catch (error: any) { fastify.log.error(error.message) diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.hardDelete.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.hardDelete.ts index eaea59c..02edd39 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.hardDelete.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.hardDelete.ts @@ -28,7 +28,9 @@ export default (fastify: PluginFastifyInstance) => { return payloadWithTag } else { - throw new ForbiddenError("Only Admins can hard Delete") + throw new ForbiddenError( + "Only admins can hard delete this core datatype", + ) } } catch (error: any) { fastify.log.error(error.message) diff --git a/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.hardDelete.ts b/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.hardDelete.ts index 42246ad..94ab358 100644 --- a/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.hardDelete.ts +++ b/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.hardDelete.ts @@ -28,7 +28,9 @@ export default (fastify: PluginFastifyInstance) => { return payloadWithUser } else { - throw new ForbiddenError("Only Admins can hard Delete") + throw new ForbiddenError( + "Only admins can hard delete this core datatype", + ) } } catch (error: any) { fastify.log.error(error.message) -- GitLab From 9d09113739f9d68d76c3994f300c565bfd4f3408 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 6 Nov 2025 17:50:45 +0100 Subject: [PATCH 10/11] fix: hardDeletion Notifications --- services/frontend/src/core/lib/data/responseTypeHandler.ts | 2 +- services/frontend/src/core/lib/userNotifier.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/services/frontend/src/core/lib/data/responseTypeHandler.ts b/services/frontend/src/core/lib/data/responseTypeHandler.ts index dadcd29..7b7edb4 100644 --- a/services/frontend/src/core/lib/data/responseTypeHandler.ts +++ b/services/frontend/src/core/lib/data/responseTypeHandler.ts @@ -162,7 +162,7 @@ const handler = { requestId, data, }) - const deleted = data.deleted || [] + const deleted = data.items || [] $store.dispatch({ type: "data/hardDelete", payload: deleted }) if (notify) { userNotifier.dataDelete(isOwnRequest, isSystemAction, deleted) diff --git a/services/frontend/src/core/lib/userNotifier.ts b/services/frontend/src/core/lib/userNotifier.ts index 0d2fb42..b162115 100644 --- a/services/frontend/src/core/lib/userNotifier.ts +++ b/services/frontend/src/core/lib/userNotifier.ts @@ -129,7 +129,7 @@ const userConfigUpdate = (isAnswerToOwnRequest, isSystemAction, items) => color: "info", }) -const dataDelete = (isAnswerToOwnRequest, isSystemAction, deletedItems) => +const dataDelete = (isAnswerToOwnRequest, isSystemAction, deletedItems) => { deletedItems.forEach((item) => { if (isAnswerToOwnRequest) { $eventbus.emit("ui/addToSnackbar", { @@ -153,6 +153,7 @@ const dataDelete = (isAnswerToOwnRequest, isSystemAction, deletedItems) => }) } }) +} const dataMove = (isAnswerToOwnRequest, isSystemAction, items) => { items.forEach((item) => { -- GitLab From 691fc3ea71f0d2d4d8e4dbb090c3b30c2b6afcb7 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 6 Nov 2025 17:56:00 +0100 Subject: [PATCH 11/11] chore: udpate changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f076ce..a4f1db2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.1.17] - 05.11.2025 Overview of all changes +## [0.1.17] - 06.11.2025 Overview of all changes ### Fixed - Frontend: axios Delete handling - Frontend: retrieval of deleted compositions/interactionLinks during socket join +- Frontend: notifications for delete events now show name +- ### Added -- GitLab