@raclettejs/core 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -2
- package/dist/cli.js +104 -104
- package/dist/cli.js.map +7 -0
- package/dist/index.js +4 -4
- package/dist/index.js.map +7 -0
- package/package.json +32 -31
- package/services/backend/package.json +35 -45
- package/services/backend/src/core/payload/payloadTypes.ts +7 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.get.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.getAll.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.model.ts +5 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.schema.ts +1 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.get.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.getAll.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/helpers/payload.ts +6 -2
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.get.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.getAll.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.get.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.getAll.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/index.ts +2 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.get.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.getAll.ts +6 -2
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/tag.service.ts +22 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.get.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.getAll.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.remove.ts +3 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/user.service.ts +1 -2
- package/services/backend/src/corePlugins/raclette__core/frontend/generated-config.ts +12 -12
- package/services/backend/src/domains/index.ts +3 -0
- package/services/backend/src/domains/system/collections.service.ts +119 -0
- package/services/backend/src/domains/system/index.ts +8 -0
- package/services/backend/src/domains/system/routes/index.ts +5 -0
- package/services/backend/src/domains/system/routes/route.collection.getAll.ts +61 -0
- package/services/backend/src/domains/system/routes/route.collection.remove.ts +93 -0
- package/services/backend/src/types/custom-fastify.d.ts +1 -0
- package/services/backend/src/utils/request.utils.ts +28 -1
- package/services/backend/yarn.lock +431 -1180
- package/services/frontend/package.json +29 -40
- package/services/frontend/src/core/constants/ApiClient.ts +6 -1
- package/services/frontend/src/core/lib/data/dataApi.ts +8 -1
- package/services/frontend/src/core/lib/data/fetchDataHandler.ts +58 -27
- package/services/frontend/src/core/lib/data/queryApi.ts +29 -8
- package/services/frontend/src/core/lib/data/responseTypeHandler.ts +27 -4
- package/services/frontend/src/core/lib/data/writeDataHandler.ts +1 -1
- package/services/frontend/src/core/lib/dataHelper.ts +8 -1
- package/services/frontend/src/core/lib/easteregg.ts +1 -1
- package/services/frontend/src/core/lib/eventEmitter.ts +1 -0
- package/services/frontend/src/core/lib/eventWhitelist.ts +11 -1
- package/services/frontend/src/core/lib/logger.ts +11 -3
- package/services/frontend/src/core/lib/userNotifier.ts +17 -17
- package/services/frontend/src/core/main.ts +3 -3
- package/services/frontend/src/core/setup/plugin-system/api/wrappers.ts +18 -1
- package/services/frontend/src/core/setup/plugin-system/registration/index.ts +1 -1
- package/services/frontend/src/core/setup/socketClient.ts +2 -2
- package/services/frontend/src/core/setup/socketEvents.ts +2 -2
- package/services/frontend/src/core/store/reducers/compositions/selectors.ts +2 -2
- package/services/frontend/src/core/store/reducers/data/index.ts +1 -1
- package/services/frontend/src/core/store/reducers/data/reducers.ts +3 -3
- package/services/frontend/src/core/store/reducers/queries/effects.ts +20 -1
- package/services/frontend/src/core/store/reducers/queries/index.ts +1 -0
- package/services/frontend/src/core/store/reducers/queries/reducers.ts +28 -1
- package/services/frontend/src/core/store/reducers/queriesCache/queryCacheHelper.ts +5 -4
- package/services/frontend/src/core/store/reducers/queriesCache/reducers.ts +4 -3
- package/services/frontend/src/core/store/state.ts +1 -1
- package/services/frontend/src/core/types/ApiClient.ts +1 -1
- package/services/frontend/src/core/types/DataApi.ts +4 -0
- package/services/frontend/src/core/types/Queries.ts +1 -0
- package/services/frontend/src/orchestrator/ProductOrchestrator.vue +19 -7
- package/services/frontend/src/orchestrator/WelcomeScreen.vue +39 -41
- package/services/frontend/src/orchestrator/assets/styles/tailwindStyles.css +3 -0
- package/services/frontend/src/orchestrator/assets/styles/themes/dark.ts +2 -2
- package/services/frontend/src/orchestrator/assets/styles/vuetifyStyles.scss +0 -1
- package/services/frontend/src/orchestrator/components/composition/CompositionOverlay.vue +11 -2
- package/services/frontend/src/orchestrator/components/composition/WidgetsLayoutLoader.vue +167 -58
- package/services/frontend/src/orchestrator/components/menu/DevIndicator.vue +122 -7
- package/services/frontend/src/orchestrator/components/menu/UserMenu.vue +38 -20
- package/services/frontend/src/orchestrator/components/snackbar/SnackStack.vue +17 -5
- package/services/frontend/src/orchestrator/composables/useCurrentComposition.ts +32 -4
- package/services/frontend/src/orchestrator/composables/usePageNavigation.ts +5 -1
- package/services/frontend/src/orchestrator/composables/usePluginApi.ts +125 -26
- package/services/frontend/src/orchestrator/composables/useRouteState.ts +0 -1
- package/services/frontend/src/orchestrator/composables/useVueQueryObservableHelper.ts +2 -0
- package/services/frontend/src/orchestrator/composables/useVueWriteOperationHelper.ts +5 -3
- package/services/frontend/src/orchestrator/composables/useWidgets/helperFunctions.ts +4 -1
- package/services/frontend/src/orchestrator/helpers/loginUserNotifications.ts +1 -1
- package/services/frontend/src/orchestrator/helpers/uiHelper.ts +1 -1
- package/services/frontend/src/orchestrator/i18n/de-DE.json +2 -1
- package/services/frontend/src/orchestrator/i18n/en-EU.json +2 -1
- package/services/frontend/src/orchestrator/i18n/sk.json +2 -1
- package/services/frontend/src/orchestrator/i18n/tl-TL.json +1 -1
- package/services/frontend/src/orchestrator/router/routeParserHelper.ts +4 -1
- package/services/frontend/src/orchestrator/router/routeStore.ts +78 -11
- package/services/frontend/tsconfig.app.json +1 -0
- package/services/frontend/yarn.lock +796 -1872
- package/yarn.lock +435 -618
|
@@ -36,6 +36,7 @@ const baseCompositionSchema = {
|
|
|
36
36
|
tags: Type.Array(Type.String(), { default: [] }),
|
|
37
37
|
icon: Type.Optional(Type.String()),
|
|
38
38
|
pathname: Type.Record(Type.String(), Type.String(), { default: {} }),
|
|
39
|
+
slotLayout: Type.Optional(Type.String()),
|
|
39
40
|
status: Type.Enum(
|
|
40
41
|
{ live: "live", test: "test", inProgress: "inProgress" },
|
|
41
42
|
{ default: "inProgress" },
|
|
@@ -4,9 +4,9 @@ import type {
|
|
|
4
4
|
FrontendPayloadRequestData,
|
|
5
5
|
} from "@c/payload/payloadTypes"
|
|
6
6
|
import type { PluginFastifyInstance } from "types"
|
|
7
|
+
import type { Model } from "mongoose"
|
|
7
8
|
import { v4 as uuidv4, validate } from "uuid"
|
|
8
9
|
import { createCompositionPayload } from "./helpers/payload"
|
|
9
|
-
import { Model } from "mongoose"
|
|
10
10
|
|
|
11
11
|
export type CompositionBody = Partial<Omit<CompositionType, "_id">> & {
|
|
12
12
|
_id?: string
|
|
@@ -5,9 +5,13 @@ export const registerPayload = (fastify: PluginFastifyInstance) => {
|
|
|
5
5
|
fastify.registerPayloadHandler<InteractionLink>("interactionLink", {
|
|
6
6
|
type: "interactionLink",
|
|
7
7
|
displayName: (interactionLink: InteractionLink) =>
|
|
8
|
-
interactionLink.description ||
|
|
8
|
+
interactionLink.description ||
|
|
9
|
+
`CompositionType: ${interactionLink.slotType}` ||
|
|
10
|
+
"No Description",
|
|
9
11
|
completion: (interactionLink: InteractionLink) =>
|
|
10
|
-
interactionLink.description ||
|
|
12
|
+
interactionLink.description ||
|
|
13
|
+
interactionLink.slotType ||
|
|
14
|
+
"No Description",
|
|
11
15
|
fields: (item: InteractionLink) => ({
|
|
12
16
|
owner: item.author,
|
|
13
17
|
}),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PluginFastifyInstance } from "types"
|
|
1
|
+
import type { PluginFastifyInstance } from "types"
|
|
2
2
|
import registerRoutes from "./routes"
|
|
3
3
|
import { createModels } from "./tag.model"
|
|
4
4
|
import { createTagService } from "./tag.service"
|
|
@@ -41,6 +41,7 @@ const registerTag = async (fastify: PluginFastifyInstance) => {
|
|
|
41
41
|
operations: {
|
|
42
42
|
readAll: tagService._readTags.bind(tagService),
|
|
43
43
|
read: tagService._readTag.bind(tagService),
|
|
44
|
+
readByTitle: tagService._readTagByTitle.bind(tagService),
|
|
44
45
|
create: tagService._createTag.bind(tagService),
|
|
45
46
|
update: tagService._updateTag.bind(tagService),
|
|
46
47
|
delete: tagService._removeTag.bind(tagService),
|
|
@@ -6,7 +6,11 @@ export default (fastify: PluginFastifyInstance) => {
|
|
|
6
6
|
|
|
7
7
|
const handler = async (req: FastifyRequest, reply: FastifyReply) => {
|
|
8
8
|
try {
|
|
9
|
-
const payload = await tagService.readTags(
|
|
9
|
+
const payload = await tagService.readTags(
|
|
10
|
+
fastify,
|
|
11
|
+
req.requestParams,
|
|
12
|
+
req.query,
|
|
13
|
+
)
|
|
10
14
|
|
|
11
15
|
if (!payload || payload.length === 0) {
|
|
12
16
|
return reply.code(204).send()
|
|
@@ -24,7 +28,7 @@ export default (fastify: PluginFastifyInstance) => {
|
|
|
24
28
|
handler,
|
|
25
29
|
onRequest: [fastify.authenticate],
|
|
26
30
|
config: {
|
|
27
|
-
type: "
|
|
31
|
+
type: "dataRead",
|
|
28
32
|
},
|
|
29
33
|
schema: {
|
|
30
34
|
summary: "Get all tags for the given project.",
|
package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/tag.service.ts
CHANGED
|
@@ -101,6 +101,27 @@ export class TagService {
|
|
|
101
101
|
return createTagPayload(fastify, [tag], requestData)
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Core function to read a tag by name – used for internal system functions mainly
|
|
106
|
+
*/
|
|
107
|
+
async _readTagByTitle(
|
|
108
|
+
fastify: PluginFastifyInstance,
|
|
109
|
+
title: string,
|
|
110
|
+
): Promise<TagType> {
|
|
111
|
+
try {
|
|
112
|
+
const tags = await this.tagModel.find({ title }).lean()
|
|
113
|
+
|
|
114
|
+
if (!tags) {
|
|
115
|
+
throw new Error(`Tag with title ${title} not found`)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return tags[0]
|
|
119
|
+
} catch (error: any) {
|
|
120
|
+
fastify.log.error(error.message)
|
|
121
|
+
throw error
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
104
125
|
/**
|
|
105
126
|
* Core function to read tags by ID or filter parameters (returns raw data)
|
|
106
127
|
*/
|
|
@@ -153,7 +174,7 @@ export class TagService {
|
|
|
153
174
|
|
|
154
175
|
return createTagPayload(fastify, [tag], requestData)
|
|
155
176
|
} else {
|
|
156
|
-
const tags = await this._readTags(fastify, filter)
|
|
177
|
+
const tags = await this._readTags(fastify, filter, requestData.options)
|
|
157
178
|
|
|
158
179
|
return createTagPayload(fastify, tags, requestData)
|
|
159
180
|
}
|
|
@@ -19,6 +19,9 @@ export default (fastify: PluginFastifyInstance) => {
|
|
|
19
19
|
reply: FastifyReply,
|
|
20
20
|
) => {
|
|
21
21
|
try {
|
|
22
|
+
if (!req.user.isAdmin) {
|
|
23
|
+
throw new ForbiddenError("Only Admins can use this feature")
|
|
24
|
+
}
|
|
22
25
|
if (req.user._id === req.params._id) {
|
|
23
26
|
throw new ForbiddenError("A user cannot remove itself")
|
|
24
27
|
}
|
package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/user.service.ts
CHANGED
|
@@ -168,9 +168,8 @@ export class UserService {
|
|
|
168
168
|
fastify: PluginFastifyInstance,
|
|
169
169
|
requestData: FrontendPayloadRequestData,
|
|
170
170
|
filter: Record<string, any> = {},
|
|
171
|
-
options: QueryOptions = {},
|
|
172
171
|
): Promise<FrontendPayload<PublicUser[]>> {
|
|
173
|
-
const users = await this._readUsers(fastify, filter, options)
|
|
172
|
+
const users = await this._readUsers(fastify, filter, requestData.options)
|
|
174
173
|
|
|
175
174
|
return createUserPayload(fastify, users, requestData)
|
|
176
175
|
}
|
|
@@ -15,12 +15,12 @@ export default {
|
|
|
15
15
|
getAll: {
|
|
16
16
|
target: "/raclette__core/account/all",
|
|
17
17
|
method: "get",
|
|
18
|
-
storeActionType: "
|
|
18
|
+
storeActionType: "dataRead"
|
|
19
19
|
},
|
|
20
20
|
get: {
|
|
21
21
|
target: (payload) => `/raclette__core/account/${payload._id}`,
|
|
22
22
|
method: "get",
|
|
23
|
-
storeActionType: "
|
|
23
|
+
storeActionType: "dataRead"
|
|
24
24
|
},
|
|
25
25
|
update: {
|
|
26
26
|
target: (payload) => `/raclette__core/account/${payload._id}`,
|
|
@@ -83,12 +83,12 @@ export default {
|
|
|
83
83
|
getAll: {
|
|
84
84
|
target: "/raclette__core/interactionLink/all",
|
|
85
85
|
method: "get",
|
|
86
|
-
storeActionType: "
|
|
86
|
+
storeActionType: "dataRead"
|
|
87
87
|
},
|
|
88
88
|
get: {
|
|
89
89
|
target: (payload) => `/raclette__core/interactionLink/${payload._id}`,
|
|
90
90
|
method: "get",
|
|
91
|
-
storeActionType: "
|
|
91
|
+
storeActionType: "dataRead"
|
|
92
92
|
},
|
|
93
93
|
update: {
|
|
94
94
|
target: (payload) => `/raclette__core/interactionLink/${payload._id}`,
|
|
@@ -146,12 +146,12 @@ export default {
|
|
|
146
146
|
getAll: {
|
|
147
147
|
target: "/raclette__core/project/all",
|
|
148
148
|
method: "get",
|
|
149
|
-
storeActionType: "
|
|
149
|
+
storeActionType: "dataRead"
|
|
150
150
|
},
|
|
151
151
|
get: {
|
|
152
152
|
target: (payload) => `/raclette__core/project/${payload._id}`,
|
|
153
153
|
method: "get",
|
|
154
|
-
storeActionType: "
|
|
154
|
+
storeActionType: "dataRead"
|
|
155
155
|
},
|
|
156
156
|
create: {
|
|
157
157
|
target: "/raclette__core/project",
|
|
@@ -209,12 +209,12 @@ export default {
|
|
|
209
209
|
getAll: {
|
|
210
210
|
target: "/raclette__core/tag/all",
|
|
211
211
|
method: "get",
|
|
212
|
-
storeActionType: "
|
|
212
|
+
storeActionType: "dataRead"
|
|
213
213
|
},
|
|
214
214
|
get: {
|
|
215
215
|
target: (payload) => `/raclette__core/tag/${payload._id}`,
|
|
216
216
|
method: "get",
|
|
217
|
-
storeActionType: "
|
|
217
|
+
storeActionType: "dataRead"
|
|
218
218
|
},
|
|
219
219
|
update: {
|
|
220
220
|
target: (payload) => `/raclette__core/tag/${payload._id}`,
|
|
@@ -272,12 +272,12 @@ export default {
|
|
|
272
272
|
getAll: {
|
|
273
273
|
target: "/raclette__core/user/all",
|
|
274
274
|
method: "get",
|
|
275
|
-
storeActionType: "
|
|
275
|
+
storeActionType: "dataRead"
|
|
276
276
|
},
|
|
277
277
|
get: {
|
|
278
278
|
target: (payload) => `/raclette__core/user/${payload._id}`,
|
|
279
279
|
method: "get",
|
|
280
|
-
storeActionType: "
|
|
280
|
+
storeActionType: "dataRead"
|
|
281
281
|
},
|
|
282
282
|
update: {
|
|
283
283
|
target: (payload) => `/raclette__core/user/${payload._id}`,
|
|
@@ -335,12 +335,12 @@ export default {
|
|
|
335
335
|
getAll: {
|
|
336
336
|
target: "/raclette__core/composition/all",
|
|
337
337
|
method: "get",
|
|
338
|
-
storeActionType: "
|
|
338
|
+
storeActionType: "dataRead"
|
|
339
339
|
},
|
|
340
340
|
get: {
|
|
341
341
|
target: (payload) => `/raclette__core/composition/${payload._id}`,
|
|
342
342
|
method: "get",
|
|
343
|
-
storeActionType: "
|
|
343
|
+
storeActionType: "dataRead"
|
|
344
344
|
},
|
|
345
345
|
update: {
|
|
346
346
|
target: (payload) => `/raclette__core/composition/${payload._id}`,
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { PluginFastifyInstance } from "types"
|
|
2
|
+
import mongoose, { type Connection } from "mongoose"
|
|
3
|
+
|
|
4
|
+
export interface CollectionInfo {
|
|
5
|
+
name: string
|
|
6
|
+
count: number
|
|
7
|
+
size: number
|
|
8
|
+
indexes: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class CollectionService {
|
|
12
|
+
private connection: Connection
|
|
13
|
+
|
|
14
|
+
constructor(connection: Connection) {
|
|
15
|
+
this.connection = connection
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get all collection names and their metadata
|
|
20
|
+
*/
|
|
21
|
+
async getCollections(
|
|
22
|
+
fastify: PluginFastifyInstance,
|
|
23
|
+
): Promise<CollectionInfo[]> {
|
|
24
|
+
try {
|
|
25
|
+
const db = this.connection.db
|
|
26
|
+
const collections = await db.listCollections().toArray()
|
|
27
|
+
|
|
28
|
+
const collectionInfos: CollectionInfo[] = []
|
|
29
|
+
|
|
30
|
+
for (const collection of collections) {
|
|
31
|
+
const collectionName = collection.name
|
|
32
|
+
|
|
33
|
+
// Get document count
|
|
34
|
+
const count = await db.collection(collectionName).countDocuments()
|
|
35
|
+
|
|
36
|
+
// Get collection stats using MongoDB command
|
|
37
|
+
const stats = await db.command({ collStats: collectionName })
|
|
38
|
+
|
|
39
|
+
collectionInfos.push({
|
|
40
|
+
name: collectionName,
|
|
41
|
+
count: count || 0,
|
|
42
|
+
size: stats.size || 0,
|
|
43
|
+
indexes: stats.nindexes || 0,
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fastify.log.info(`[API] Retrieved ${collectionInfos.length} collections`)
|
|
48
|
+
|
|
49
|
+
return collectionInfos
|
|
50
|
+
} catch (error: any) {
|
|
51
|
+
fastify.log.error(error.message)
|
|
52
|
+
throw error
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Drop specific collections
|
|
58
|
+
*/
|
|
59
|
+
async dropCollections(
|
|
60
|
+
fastify: PluginFastifyInstance,
|
|
61
|
+
collectionNames: string[],
|
|
62
|
+
): Promise<{ dropped: string[]; failed: string[] }> {
|
|
63
|
+
try {
|
|
64
|
+
const db = this.connection.db
|
|
65
|
+
const dropped: string[] = []
|
|
66
|
+
const failed: string[] = []
|
|
67
|
+
|
|
68
|
+
for (const collectionName of collectionNames) {
|
|
69
|
+
try {
|
|
70
|
+
// Check if collection exists
|
|
71
|
+
const collections = await db
|
|
72
|
+
.listCollections({ name: collectionName })
|
|
73
|
+
.toArray()
|
|
74
|
+
|
|
75
|
+
if (collections.length === 0) {
|
|
76
|
+
fastify.log.warn(`Collection ${collectionName} does not exist`)
|
|
77
|
+
failed.push(collectionName)
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Drop the collection
|
|
82
|
+
await db.collection(collectionName).drop()
|
|
83
|
+
dropped.push(collectionName)
|
|
84
|
+
fastify.log.info(`[API] Dropped collection: ${collectionName}`)
|
|
85
|
+
} catch (error: any) {
|
|
86
|
+
fastify.log.error(
|
|
87
|
+
`Failed to drop collection ${collectionName}: ${error.message}`,
|
|
88
|
+
)
|
|
89
|
+
failed.push(collectionName)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { dropped, failed }
|
|
94
|
+
} catch (error: any) {
|
|
95
|
+
fastify.log.error(error.message)
|
|
96
|
+
throw error
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Drop all collections (use with extreme caution!)
|
|
102
|
+
*/
|
|
103
|
+
async dropAllCollections(
|
|
104
|
+
fastify: PluginFastifyInstance,
|
|
105
|
+
): Promise<{ dropped: string[]; failed: string[] }> {
|
|
106
|
+
try {
|
|
107
|
+
const collections = await this.getCollections(fastify)
|
|
108
|
+
const collectionNames = collections.map((c) => c.name)
|
|
109
|
+
|
|
110
|
+
return await this.dropCollections(fastify, collectionNames)
|
|
111
|
+
} catch (error: any) {
|
|
112
|
+
fastify.log.error(error.message)
|
|
113
|
+
throw error
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const createCollectionService = (connection: Connection) =>
|
|
119
|
+
new CollectionService(connection)
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import type { FastifyInstance } from "fastify"
|
|
2
|
+
import mongoose from "mongoose"
|
|
2
3
|
import registerRoutes from "./routes"
|
|
4
|
+
import { createCollectionService } from "./collections.service"
|
|
3
5
|
|
|
4
6
|
const registerSystem = (fastify: FastifyInstance) => {
|
|
5
7
|
registerRoutes(fastify)
|
|
8
|
+
|
|
9
|
+
// create and pass in the model to your service
|
|
10
|
+
const collectionService = createCollectionService(mongoose.connection)
|
|
11
|
+
|
|
12
|
+
// decorate the service to the instance, so we can easily call it everywhere
|
|
13
|
+
fastify.core.collectionService = collectionService
|
|
6
14
|
}
|
|
7
15
|
|
|
8
16
|
export default registerSystem
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
import deploy from "./route.deploy"
|
|
8
8
|
import login from "./route.user.login"
|
|
9
9
|
import checkToken from "./route.user.checkToken"
|
|
10
|
+
import getAllCollections from "./route.collection.getAll"
|
|
11
|
+
import deleteCollections from "./route.collection.remove"
|
|
10
12
|
|
|
11
13
|
const registerRoutes = async (fastify: FastifyInstance) => {
|
|
12
14
|
await fastify.get("/system/deploy", deploy(fastify))
|
|
@@ -17,5 +19,8 @@ const registerRoutes = async (fastify: FastifyInstance) => {
|
|
|
17
19
|
await fastify.get("/admin/cache/blocks", getBlockedLogins(fastify))
|
|
18
20
|
await fastify.delete("/admin/cache/blocks/:key", resetLoginBlock(fastify))
|
|
19
21
|
await fastify.delete("/admin/cache/blocks", resetAllLoginBlocks(fastify))
|
|
22
|
+
|
|
23
|
+
await fastify.get("/admin/collections/all", getAllCollections(fastify))
|
|
24
|
+
await fastify.delete("/admin/collections", deleteCollections(fastify))
|
|
20
25
|
}
|
|
21
26
|
export default registerRoutes
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { FastifyReply, FastifyRequest } from "fastify"
|
|
2
|
+
import { PluginFastifyInstance } from "types"
|
|
3
|
+
import { ForbiddenError } from "@/utils/errors"
|
|
4
|
+
|
|
5
|
+
export default (fastify: PluginFastifyInstance) => {
|
|
6
|
+
const collectionService = fastify.core.collectionService
|
|
7
|
+
|
|
8
|
+
const handler = async (req: FastifyRequest, reply: FastifyReply) => {
|
|
9
|
+
try {
|
|
10
|
+
// Restrict to admin users only
|
|
11
|
+
if (!req.user.isAdmin) {
|
|
12
|
+
throw new ForbiddenError(
|
|
13
|
+
"Only Admins can access collection information",
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const collections = await collectionService.getCollections(fastify)
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
success: true,
|
|
21
|
+
data: collections,
|
|
22
|
+
total: collections.length,
|
|
23
|
+
}
|
|
24
|
+
} catch (error: any) {
|
|
25
|
+
fastify.log.error(error.message)
|
|
26
|
+
return reply.internalServerError(error.message)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
handler,
|
|
32
|
+
onRequest: [fastify.authenticate],
|
|
33
|
+
schema: {
|
|
34
|
+
summary: "Get all collections",
|
|
35
|
+
description:
|
|
36
|
+
"Retrieve all MongoDB collections with metadata (admin only, debug feature)",
|
|
37
|
+
tags: ["debug/collections"],
|
|
38
|
+
response: {
|
|
39
|
+
200: {
|
|
40
|
+
type: "object",
|
|
41
|
+
properties: {
|
|
42
|
+
success: { type: "boolean" },
|
|
43
|
+
data: {
|
|
44
|
+
type: "array",
|
|
45
|
+
items: {
|
|
46
|
+
type: "object",
|
|
47
|
+
properties: {
|
|
48
|
+
name: { type: "string" },
|
|
49
|
+
count: { type: "number" },
|
|
50
|
+
size: { type: "number" },
|
|
51
|
+
indexes: { type: "number" },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
total: { type: "number" },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
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 BodySchema = Type.Object({
|
|
8
|
+
collections: Type.Array(Type.String(), {
|
|
9
|
+
description: "Array of collection names to drop",
|
|
10
|
+
minItems: 1,
|
|
11
|
+
}),
|
|
12
|
+
dropAll: Type.Optional(
|
|
13
|
+
Type.Boolean({
|
|
14
|
+
description:
|
|
15
|
+
"If true, drops all collections (overrides collections array)",
|
|
16
|
+
}),
|
|
17
|
+
),
|
|
18
|
+
})
|
|
19
|
+
type Body = Static<typeof BodySchema>
|
|
20
|
+
|
|
21
|
+
export default (fastify: PluginFastifyInstance) => {
|
|
22
|
+
const collectionService = fastify.core.collectionService
|
|
23
|
+
|
|
24
|
+
const handler = async (
|
|
25
|
+
req: FastifyRequest<{
|
|
26
|
+
Body: Body
|
|
27
|
+
}>,
|
|
28
|
+
reply: FastifyReply,
|
|
29
|
+
) => {
|
|
30
|
+
try {
|
|
31
|
+
// Restrict to admin users only
|
|
32
|
+
if (!req.user.isAdmin) {
|
|
33
|
+
throw new ForbiddenError("Only Admins can drop collections")
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let result
|
|
37
|
+
|
|
38
|
+
if (req.body.dropAll) {
|
|
39
|
+
fastify.log.warn(
|
|
40
|
+
`[API] Admin ${req.user._id} is dropping ALL collections`,
|
|
41
|
+
)
|
|
42
|
+
result = await collectionService.dropAllCollections(fastify)
|
|
43
|
+
} else {
|
|
44
|
+
fastify.log.warn(
|
|
45
|
+
`[API] Admin ${req.user._id} is dropping collections: ${req.body.collections.join(", ")}`,
|
|
46
|
+
)
|
|
47
|
+
result = await collectionService.dropCollections(
|
|
48
|
+
fastify,
|
|
49
|
+
req.body.collections,
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
success: true,
|
|
55
|
+
dropped: result.dropped,
|
|
56
|
+
failed: result.failed,
|
|
57
|
+
message: `Successfully dropped ${result.dropped.length} collection(s)`,
|
|
58
|
+
}
|
|
59
|
+
} catch (error: any) {
|
|
60
|
+
fastify.log.error(error.message)
|
|
61
|
+
return reply.internalServerError(error.message)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
handler,
|
|
67
|
+
onRequest: [fastify.authenticate],
|
|
68
|
+
schema: {
|
|
69
|
+
summary: "Drop collections",
|
|
70
|
+
description:
|
|
71
|
+
"Drop selected MongoDB collections (admin only, debug feature - USE WITH CAUTION)",
|
|
72
|
+
tags: ["debug/collections"],
|
|
73
|
+
body: BodySchema,
|
|
74
|
+
response: {
|
|
75
|
+
200: {
|
|
76
|
+
type: "object",
|
|
77
|
+
properties: {
|
|
78
|
+
success: { type: "boolean" },
|
|
79
|
+
dropped: {
|
|
80
|
+
type: "array",
|
|
81
|
+
items: { type: "string" },
|
|
82
|
+
},
|
|
83
|
+
failed: {
|
|
84
|
+
type: "array",
|
|
85
|
+
items: { type: "string" },
|
|
86
|
+
},
|
|
87
|
+
message: { type: "string" },
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
}
|