@raclettejs/core 0.1.22 → 0.1.24
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 +31 -0
- package/bin/cli.js +2 -2
- package/dist/cli.js +136 -155
- package/dist/cli.js.map +4 -4
- package/dist/index.js +15 -16
- package/dist/index.js.map +4 -4
- package/package.json +24 -22
- package/services/backend/.yarn/install-state.gz +0 -0
- package/services/backend/package.json +21 -19
- package/services/backend/src/core/contracting/contractRegistrar.ts +1 -1
- package/services/backend/src/core/pluginSystem/configGenerator/generatorTypes.ts +1 -0
- package/services/backend/src/core/pluginSystem/configGenerator/index.ts +2 -2
- package/services/backend/src/core/pluginSystem/pluginLoader.ts +99 -44
- package/services/backend/src/core/pluginSystem/pluginTypes.ts +1 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/index.ts +2 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.patchOwn.ts +68 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/user.model.ts +1 -1
- package/services/backend/tsconfig.json +1 -0
- package/services/backend/yarn.lock +698 -1061
- package/services/frontend/.yarn/install-state.gz +0 -0
- package/services/frontend/eslint.config.js +9 -0
- package/services/frontend/index.html +3 -3
- package/services/frontend/package.json +30 -28
- package/services/frontend/src/core/setup/plugin-system/discovery/app-plugins.ts +43 -4
- package/services/frontend/src/orchestrator/ProductOrchestrator.vue +10 -11
- package/services/frontend/src/orchestrator/assets/styles/layers.css +11 -0
- package/services/frontend/src/orchestrator/assets/styles/tailwindStyles.css +5 -11
- package/services/frontend/src/orchestrator/assets/styles/vuetifyStyles.scss +0 -4
- package/services/frontend/src/orchestrator/components/composition/WidgetsLayoutLoader.vue +5 -18
- package/services/frontend/src/orchestrator/composables/useWidgets/helperFunctions.ts +54 -5
- package/services/frontend/src/orchestrator/helpers/index.ts +1 -0
- package/services/frontend/src/orchestrator/helpers/widgetVisuals.ts +71 -0
- package/services/frontend/src/orchestrator/setup/vuetify.ts +2 -0
- package/services/frontend/src/orchestrator/types/Widgets.ts +5 -1
- package/services/frontend/vite.config.ts +22 -9
- package/services/frontend/yarn.lock +0 -5224
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raclettejs/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.24",
|
|
4
4
|
"description": "racletteJS core package",
|
|
5
5
|
"repository": "https://gitlab.com/raclettejs/core",
|
|
6
6
|
"author": "Pacifico Digital Explorations GmbH <info@raclettejs.com>",
|
|
@@ -59,40 +59,42 @@
|
|
|
59
59
|
},
|
|
60
60
|
"config": {},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@inquirer/prompts": "8.2
|
|
62
|
+
"@inquirer/prompts": "8.3.2",
|
|
63
63
|
"@types/mustache": "4.2.6",
|
|
64
64
|
"chalk": "5.6.2",
|
|
65
|
-
"chokidar": "
|
|
65
|
+
"chokidar": "5.0.0",
|
|
66
66
|
"cli-highlight": "2.1.11",
|
|
67
|
-
"commander": "14.0.
|
|
68
|
-
"dotenv": "17.
|
|
69
|
-
"esbuild": "0.
|
|
70
|
-
"fs-extra": "11.3.
|
|
71
|
-
"globby": "
|
|
72
|
-
"js-yaml": "4.1.
|
|
67
|
+
"commander": "14.0.3",
|
|
68
|
+
"dotenv": "17.3.1",
|
|
69
|
+
"esbuild": "0.27.4",
|
|
70
|
+
"fs-extra": "11.3.4",
|
|
71
|
+
"globby": "16.1.1",
|
|
72
|
+
"js-yaml": "4.1.1",
|
|
73
73
|
"mustache": "4.2.0",
|
|
74
|
-
"ramda": "0.
|
|
74
|
+
"ramda": "0.32.0",
|
|
75
75
|
"tsc-alias": "1.8.16"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
|
+
"@emnapi/core": "1.9.2",
|
|
79
|
+
"@emnapi/runtime": "1.9.2",
|
|
78
80
|
"@eslint/js": "9.35.0",
|
|
79
|
-
"@raclettejs/types": "^0.1.
|
|
81
|
+
"@raclettejs/types": "^0.1.24",
|
|
80
82
|
"@types/fs-extra": "11.0.4",
|
|
81
83
|
"@types/js-yaml": "4.0.9",
|
|
82
|
-
"@types/node": "
|
|
84
|
+
"@types/node": "25.5.0",
|
|
83
85
|
"@types/ramda": "0.31.1",
|
|
84
86
|
"eslint": "9.35.0",
|
|
85
|
-
"eslint-config-prettier": "
|
|
86
|
-
"eslint-flat-config-utils": "
|
|
87
|
+
"eslint-config-prettier": "9.1.2",
|
|
88
|
+
"eslint-flat-config-utils": "3.0.2",
|
|
87
89
|
"eslint-plugin-import": "2.32.0",
|
|
88
|
-
"eslint-plugin-prefer-arrow-functions": "3.
|
|
89
|
-
"eslint-plugin-prettier": "5.5.
|
|
90
|
-
"eslint-plugin-vue": "
|
|
91
|
-
"globals": "
|
|
90
|
+
"eslint-plugin-prefer-arrow-functions": "3.9.1",
|
|
91
|
+
"eslint-plugin-prettier": "5.5.5",
|
|
92
|
+
"eslint-plugin-vue": "9.33.0",
|
|
93
|
+
"globals": "17.4.0",
|
|
92
94
|
"husky": "9.1.7",
|
|
93
|
-
"prettier": "3.
|
|
94
|
-
"typescript": "5.9.
|
|
95
|
-
"typescript-eslint": "8.
|
|
96
|
-
"vue-eslint-parser": "
|
|
95
|
+
"prettier": "3.8.1",
|
|
96
|
+
"typescript": "5.9.3",
|
|
97
|
+
"typescript-eslint": "8.57.1",
|
|
98
|
+
"vue-eslint-parser": "9.4.3"
|
|
97
99
|
}
|
|
98
100
|
}
|
|
Binary file
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"build:assets:src": "copyfiles -u 1 'src/**/*.{json,js,yml,yaml,env*}' dist",
|
|
17
17
|
"build:assets:root": "copyfiles -u 0 'raclette.config.js' dist",
|
|
18
18
|
"build:assets": "npm run build:assets:src && npm run build:assets:root",
|
|
19
|
+
"lint": "eslint .",
|
|
19
20
|
"migrate:create": "npx migrate-mongo create -f './mongo-migrations/migrate-mongo-config.js'",
|
|
20
21
|
"migrate:up": "migrate-mongo up -f './mongo-migrations/migrate-mongo-config.js'",
|
|
21
22
|
"migrate:down": "migrate-mongo down -f './mongo-migrations/migrate-mongo-config.js'",
|
|
@@ -24,28 +25,29 @@
|
|
|
24
25
|
"typecheck:watch": "tsc --noEmit --watch"
|
|
25
26
|
},
|
|
26
27
|
"dependencies": {
|
|
27
|
-
"@fastify/cors": "11.
|
|
28
|
+
"@fastify/cors": "11.2.0",
|
|
28
29
|
"@fastify/jwt": "10.0.0",
|
|
29
30
|
"@fastify/rate-limit": "10.3.0",
|
|
30
31
|
"@fastify/schedule": "6.0.0",
|
|
31
|
-
"@fastify/sensible": "6.0.
|
|
32
|
-
"@sinclair/typebox": "0.34.
|
|
32
|
+
"@fastify/sensible": "6.0.4",
|
|
33
|
+
"@sinclair/typebox": "0.34.48",
|
|
33
34
|
"@types/vary": "1.1.3",
|
|
34
|
-
"axios": "1.13.
|
|
35
|
+
"axios": "1.13.6",
|
|
35
36
|
"bcrypt": "6.0.0",
|
|
36
37
|
"copyfiles": "2.4.1",
|
|
37
|
-
"fastify": "5.
|
|
38
|
+
"fastify": "5.8.2",
|
|
38
39
|
"fastify-custom-healthcheck": "4.0.0",
|
|
39
|
-
"
|
|
40
|
+
"fs-extra": "11.3.4",
|
|
41
|
+
"glob": "13.0.6",
|
|
40
42
|
"iovalkey": "0.3.3",
|
|
41
|
-
"migrate-mongo": "
|
|
42
|
-
"mongodb": "
|
|
43
|
-
"mongoose": "8.
|
|
44
|
-
"nanoid": "5.1.
|
|
45
|
-
"pino": "
|
|
46
|
-
"pino-pretty": "13.1.
|
|
47
|
-
"ramda": "0.
|
|
48
|
-
"socket.io": "4.8.
|
|
43
|
+
"migrate-mongo": "14.0.7",
|
|
44
|
+
"mongodb": "6.21.0",
|
|
45
|
+
"mongoose": "8.23.0",
|
|
46
|
+
"nanoid": "5.1.7",
|
|
47
|
+
"pino": "10.3.1",
|
|
48
|
+
"pino-pretty": "13.1.3",
|
|
49
|
+
"ramda": "0.32.0",
|
|
50
|
+
"socket.io": "4.8.3",
|
|
49
51
|
"toad-scheduler": "3.1.0",
|
|
50
52
|
"tsc-alias": "1.8.16",
|
|
51
53
|
"uuid": "13.0.0"
|
|
@@ -58,9 +60,9 @@
|
|
|
58
60
|
"@types/uuid": "11.0.0",
|
|
59
61
|
"eslint": "9.35.0",
|
|
60
62
|
"eslint-plugin-import": "2.32.0",
|
|
61
|
-
"eslint-plugin-prefer-arrow-functions": "3.
|
|
62
|
-
"tsx": "4.
|
|
63
|
-
"typescript": "5.9.
|
|
64
|
-
"typescript-eslint": "8.
|
|
63
|
+
"eslint-plugin-prefer-arrow-functions": "3.9.1",
|
|
64
|
+
"tsx": "4.21.0",
|
|
65
|
+
"typescript": "5.9.3",
|
|
66
|
+
"typescript-eslint": "8.57.1"
|
|
65
67
|
}
|
|
66
|
-
}
|
|
68
|
+
}
|
|
@@ -12,7 +12,7 @@ export const registerPluginContract = (
|
|
|
12
12
|
contract: PluginContract,
|
|
13
13
|
): void => {
|
|
14
14
|
if (contracts[pluginKey]) {
|
|
15
|
-
throw new Error(`Plugin contract for "${pluginKey}" is already registered
|
|
15
|
+
throw new Error(`Plugin contract for "${pluginKey}" is already registered. Make sure that each plugin is calling fastify.generatePluginContract only once! Multiple calls are not allowed.`)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
contracts[pluginKey] = contract
|
|
@@ -42,6 +42,7 @@ class FrontendConfigRegistry {
|
|
|
42
42
|
author: pluginData.author,
|
|
43
43
|
pluginKey: pluginData.key,
|
|
44
44
|
pluginPath: pluginData.absolutePath,
|
|
45
|
+
frontendPath: pluginData.frontendAbsolutePath,
|
|
45
46
|
routePrefix: pluginData.routePrefix,
|
|
46
47
|
entityMapping: options?.entityMapping || {},
|
|
47
48
|
customConfig: options?.customConfig,
|
|
@@ -451,8 +452,7 @@ const writePluginConfigFile = async (
|
|
|
451
452
|
|
|
452
453
|
const configContent = generatePluginConfigFile(dataTypes, registration)
|
|
453
454
|
const outputPath = path.join(
|
|
454
|
-
registration.
|
|
455
|
-
"frontend",
|
|
455
|
+
registration.frontendPath,
|
|
456
456
|
"generated-config.ts",
|
|
457
457
|
)
|
|
458
458
|
|
|
@@ -30,6 +30,7 @@ interface PluginInfo {
|
|
|
30
30
|
pluginName: string
|
|
31
31
|
modulePath: string
|
|
32
32
|
pluginPath: string
|
|
33
|
+
frontendPath: string
|
|
33
34
|
type: PluginType
|
|
34
35
|
metadata: PluginMetadata
|
|
35
36
|
}
|
|
@@ -75,6 +76,74 @@ const logPluginError = (
|
|
|
75
76
|
fastify.log.debug(`${createLogPrefix(type)} ${identifier}: ERROR - ${error}`)
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
const normalizeRelativeDir = (dir?: string): string | undefined =>
|
|
80
|
+
dir?.replace(/^\.\//, "")
|
|
81
|
+
|
|
82
|
+
const resolveExistingServicePath = async (
|
|
83
|
+
pluginPath: string,
|
|
84
|
+
candidates: string[],
|
|
85
|
+
): Promise<{ relativePath: string; absolutePath: string } | undefined> => {
|
|
86
|
+
for (const candidatePath of candidates) {
|
|
87
|
+
const normalizedCandidatePath = normalizeRelativeDir(candidatePath)
|
|
88
|
+
if (!normalizedCandidatePath) continue
|
|
89
|
+
|
|
90
|
+
const candidateAbsolutePath = path.join(pluginPath, normalizedCandidatePath)
|
|
91
|
+
try {
|
|
92
|
+
const stat = await fs.promises.stat(candidateAbsolutePath)
|
|
93
|
+
if (stat.isDirectory()) {
|
|
94
|
+
return {
|
|
95
|
+
relativePath: normalizedCandidatePath,
|
|
96
|
+
absolutePath: candidateAbsolutePath,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
// path missing or not accessible
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const resolveBackendPath = async (
|
|
106
|
+
pluginPath: string,
|
|
107
|
+
metadata: PluginMetadata,
|
|
108
|
+
): Promise<{ relativePath: string; absolutePath: string } | undefined> => {
|
|
109
|
+
const backendPathCandidates = getBackendPathCandidates(metadata)
|
|
110
|
+
|
|
111
|
+
return resolveExistingServicePath(pluginPath, backendPathCandidates)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const getBackendPathCandidates = (metadata: PluginMetadata): string[] => {
|
|
115
|
+
const explicitBackendDir = normalizeRelativeDir(metadata.backendDir)
|
|
116
|
+
if (explicitBackendDir) {
|
|
117
|
+
return [explicitBackendDir]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return ["backend", "src/backend"]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const resolveFrontendPath = async (
|
|
124
|
+
pluginPath: string,
|
|
125
|
+
metadata: PluginMetadata,
|
|
126
|
+
backendRelativePath?: string,
|
|
127
|
+
): Promise<string> => {
|
|
128
|
+
const explicitFrontendDir = normalizeRelativeDir(metadata.frontendDir)
|
|
129
|
+
if (explicitFrontendDir) {
|
|
130
|
+
return path.join(pluginPath, explicitFrontendDir)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const resolvedFrontendPath = await resolveExistingServicePath(pluginPath, [
|
|
134
|
+
"frontend",
|
|
135
|
+
"src/frontend",
|
|
136
|
+
])
|
|
137
|
+
if (resolvedFrontendPath) {
|
|
138
|
+
return resolvedFrontendPath.absolutePath
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Fall back to src/frontend when backend is under src, otherwise frontend.
|
|
142
|
+
const fallbackRelativePath =
|
|
143
|
+
backendRelativePath?.startsWith("src/") ? "src/frontend" : "frontend"
|
|
144
|
+
return path.join(pluginPath, fallbackRelativePath)
|
|
145
|
+
}
|
|
146
|
+
|
|
78
147
|
/**
|
|
79
148
|
* Get app plugin paths for current environment
|
|
80
149
|
*/
|
|
@@ -254,16 +323,18 @@ const collectLocalPluginsFromPath = async (
|
|
|
254
323
|
continue
|
|
255
324
|
}
|
|
256
325
|
|
|
257
|
-
// Get backend path from metadata or
|
|
258
|
-
const
|
|
259
|
-
const
|
|
326
|
+
// Get backend path from metadata or try supported defaults.
|
|
327
|
+
const resolvedBackendPath = await resolveBackendPath(pluginPath, metadata)
|
|
328
|
+
const backendRelativePath = resolvedBackendPath?.relativePath
|
|
329
|
+
const backendPath = resolvedBackendPath?.absolutePath
|
|
260
330
|
|
|
261
|
-
if (!
|
|
331
|
+
if (!backendRelativePath || !backendPath) {
|
|
332
|
+
const backendPathCandidates = getBackendPathCandidates(metadata)
|
|
262
333
|
logPluginSkipped(
|
|
263
334
|
fastify,
|
|
264
335
|
pluginType,
|
|
265
336
|
pluginIdentifier,
|
|
266
|
-
`backend directory not found: ${
|
|
337
|
+
`backend directory not found: ${backendPathCandidates.join(" or ")}`,
|
|
267
338
|
)
|
|
268
339
|
if (metadata.backendDir) {
|
|
269
340
|
// we only cancel when the dir was explicitly set
|
|
@@ -277,24 +348,6 @@ const collectLocalPluginsFromPath = async (
|
|
|
277
348
|
continue
|
|
278
349
|
}
|
|
279
350
|
|
|
280
|
-
if (!fs.statSync(backendPath).isDirectory()) {
|
|
281
|
-
logPluginSkipped(
|
|
282
|
-
fastify,
|
|
283
|
-
pluginType,
|
|
284
|
-
pluginIdentifier,
|
|
285
|
-
`backend path is not a directory: ${backendRelativePath}`,
|
|
286
|
-
)
|
|
287
|
-
if (metadata.backendDir) {
|
|
288
|
-
handlePluginError(
|
|
289
|
-
fastify,
|
|
290
|
-
`Server directory not found for plugin: ${configPath}`,
|
|
291
|
-
pluginType,
|
|
292
|
-
errorFlags,
|
|
293
|
-
)
|
|
294
|
-
}
|
|
295
|
-
continue
|
|
296
|
-
}
|
|
297
|
-
|
|
298
351
|
// Find the main module file
|
|
299
352
|
const modulePath = getFilePathWithExtension(backendPath, "index")
|
|
300
353
|
if (!modulePath) {
|
|
@@ -315,12 +368,18 @@ const collectLocalPluginsFromPath = async (
|
|
|
315
368
|
|
|
316
369
|
const author = slugify(metadata.author)
|
|
317
370
|
const pluginName = slugify(metadata.name)
|
|
371
|
+
const frontendPath = await resolveFrontendPath(
|
|
372
|
+
pluginPath,
|
|
373
|
+
metadata,
|
|
374
|
+
backendRelativePath,
|
|
375
|
+
)
|
|
318
376
|
|
|
319
377
|
plugins.push({
|
|
320
378
|
author,
|
|
321
379
|
pluginName,
|
|
322
380
|
modulePath,
|
|
323
381
|
pluginPath,
|
|
382
|
+
frontendPath,
|
|
324
383
|
type: pluginType,
|
|
325
384
|
metadata,
|
|
326
385
|
})
|
|
@@ -462,32 +521,21 @@ const collectNpmPlugins = async (
|
|
|
462
521
|
continue
|
|
463
522
|
}
|
|
464
523
|
|
|
465
|
-
// Get backend path from metadata or
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
PluginType.NPM,
|
|
473
|
-
pluginPackage,
|
|
474
|
-
`backend directory not found: ${backendRelativePath}`,
|
|
475
|
-
)
|
|
476
|
-
handlePluginError(
|
|
477
|
-
fastify,
|
|
478
|
-
`Server directory not found for npm plugin: ${pluginPackage}`,
|
|
479
|
-
PluginType.NPM,
|
|
480
|
-
errorFlags,
|
|
481
|
-
)
|
|
482
|
-
continue
|
|
483
|
-
}
|
|
524
|
+
// Get backend path from metadata or try supported defaults.
|
|
525
|
+
const resolvedBackendPath = await resolveBackendPath(
|
|
526
|
+
pluginPath,
|
|
527
|
+
pluginMetadata,
|
|
528
|
+
)
|
|
529
|
+
const backendRelativePath = resolvedBackendPath?.relativePath
|
|
530
|
+
const backendPath = resolvedBackendPath?.absolutePath
|
|
484
531
|
|
|
485
|
-
if (!
|
|
532
|
+
if (!backendRelativePath || !backendPath) {
|
|
533
|
+
const backendPathCandidates = getBackendPathCandidates(pluginMetadata)
|
|
486
534
|
logPluginSkipped(
|
|
487
535
|
fastify,
|
|
488
536
|
PluginType.NPM,
|
|
489
537
|
pluginPackage,
|
|
490
|
-
`backend
|
|
538
|
+
`backend directory not found: ${backendPathCandidates.join(" or ")}`,
|
|
491
539
|
)
|
|
492
540
|
handlePluginError(
|
|
493
541
|
fastify,
|
|
@@ -518,12 +566,18 @@ const collectNpmPlugins = async (
|
|
|
518
566
|
|
|
519
567
|
const author = slugify(pluginMetadata.author)
|
|
520
568
|
const pluginName = slugify(pluginMetadata.name)
|
|
569
|
+
const frontendPath = await resolveFrontendPath(
|
|
570
|
+
pluginPath,
|
|
571
|
+
pluginMetadata,
|
|
572
|
+
backendRelativePath,
|
|
573
|
+
)
|
|
521
574
|
|
|
522
575
|
plugins.push({
|
|
523
576
|
author,
|
|
524
577
|
pluginName,
|
|
525
578
|
modulePath,
|
|
526
579
|
pluginPath,
|
|
580
|
+
frontendPath,
|
|
527
581
|
type: PluginType.NPM,
|
|
528
582
|
metadata: pluginMetadata,
|
|
529
583
|
})
|
|
@@ -704,6 +758,7 @@ const registerCollectedPlugins = async (
|
|
|
704
758
|
collectionPrefix,
|
|
705
759
|
routePrefix,
|
|
706
760
|
absolutePath: plugin.pluginPath,
|
|
761
|
+
frontendAbsolutePath: plugin.frontendPath,
|
|
707
762
|
}
|
|
708
763
|
|
|
709
764
|
// Dynamically import the plugin module
|
package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { PluginFastifyInstance } from "types"
|
|
|
2
2
|
import get from "./route.user.get"
|
|
3
3
|
import getAll from "./route.user.getAll"
|
|
4
4
|
import patch from "./route.user.patch"
|
|
5
|
+
import patchOwn from "./route.user.patchOwn"
|
|
5
6
|
import patchLastProject from "./route.user.patchLastProject"
|
|
6
7
|
import post from "./route.user.post"
|
|
7
8
|
import remove from "./route.user.remove"
|
|
@@ -12,6 +13,7 @@ const registerRoutes = async (fastify: PluginFastifyInstance) => {
|
|
|
12
13
|
await fastify.get("/user/all", getAll(fastify))
|
|
13
14
|
await fastify.get("/user/:_id", get(fastify))
|
|
14
15
|
await fastify.patch("/user/:_id", patch(fastify))
|
|
16
|
+
await fastify.patch("/user/self", patchOwn(fastify))
|
|
15
17
|
await fastify.patch("/user/:_id/restore", restore(fastify))
|
|
16
18
|
await fastify.post("/user", post(fastify))
|
|
17
19
|
await fastify.delete("/user/:_id", remove(fastify))
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Static } from "@sinclair/typebox"
|
|
2
|
+
import type { FastifyReply, FastifyRequest } from "fastify"
|
|
3
|
+
import type { PluginFastifyInstance } from "types"
|
|
4
|
+
import { Type } from "@sinclair/typebox"
|
|
5
|
+
import { UnauthorizedError } from "@/utils/errors"
|
|
6
|
+
import { userUpdateSchema } from "../user.schema"
|
|
7
|
+
|
|
8
|
+
type UserBody = Static<typeof userUpdateSchema>
|
|
9
|
+
|
|
10
|
+
export default (fastify: PluginFastifyInstance) => {
|
|
11
|
+
const userService = fastify.custom.userService
|
|
12
|
+
|
|
13
|
+
const handler = async (
|
|
14
|
+
req: FastifyRequest<{
|
|
15
|
+
Params: {}
|
|
16
|
+
Body: UserBody
|
|
17
|
+
}>,
|
|
18
|
+
reply: FastifyReply,
|
|
19
|
+
) => {
|
|
20
|
+
try {
|
|
21
|
+
if (req.user._id !== req.body._id) {
|
|
22
|
+
throw new UnauthorizedError("not your user")
|
|
23
|
+
}
|
|
24
|
+
// only let the user update non vital fields
|
|
25
|
+
const updateObject = {}
|
|
26
|
+
if (req.body?.email?.length) {
|
|
27
|
+
updateObject.email = req.body.email
|
|
28
|
+
// TODO Creation of the field should be happening in the user.model pre save hook
|
|
29
|
+
updateObject.email_verified = false
|
|
30
|
+
}
|
|
31
|
+
if (req.body?.firstname?.length) {
|
|
32
|
+
updateObject.firstname = req.body.firstname
|
|
33
|
+
}
|
|
34
|
+
if (req.body?.lastname?.length) {
|
|
35
|
+
updateObject.lastname = req.body.lastname
|
|
36
|
+
}
|
|
37
|
+
if (req.body?.password?.length) {
|
|
38
|
+
updateObject.password = req.body.password
|
|
39
|
+
}
|
|
40
|
+
const payloadWithUser = await userService.updateUser(
|
|
41
|
+
fastify,
|
|
42
|
+
req.requestParams,
|
|
43
|
+
req.user._id,
|
|
44
|
+
updateObject,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
return payloadWithUser
|
|
48
|
+
} catch (error: any) {
|
|
49
|
+
fastify.log.error(error.message)
|
|
50
|
+
return reply.internalServerError(error.message)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
handler,
|
|
56
|
+
onRequest: [fastify.authenticate],
|
|
57
|
+
config: {
|
|
58
|
+
type: "dataUpdate",
|
|
59
|
+
broadcastChannels: ["userUpdated"],
|
|
60
|
+
},
|
|
61
|
+
schema: {
|
|
62
|
+
summary: "Update a user",
|
|
63
|
+
description: "Update specific fields of a user profile",
|
|
64
|
+
tags: ["core/user"],
|
|
65
|
+
body: userUpdateSchema,
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
}
|
package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/user.model.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { IUserMethods, UserDoc } from "./user.schema"
|
|
|
2
2
|
import bcrypt from "bcrypt"
|
|
3
3
|
import { v4 as uuidv4 } from "uuid"
|
|
4
4
|
import { randomIdGenerator } from "@/helpers"
|
|
5
|
-
import { PluginFastifyInstance } from "types"
|
|
5
|
+
import type { PluginFastifyInstance } from "types"
|
|
6
6
|
import mongoose from "mongoose"
|
|
7
7
|
|
|
8
8
|
export const MODEL_BASENAME = "user"
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"@m/*": ["./src/modules/*"],
|
|
18
18
|
"@shared/*": ["./src/shared/*"],
|
|
19
19
|
"@/*": ["./src/*"],
|
|
20
|
+
"@app/*": ["./app/*"],
|
|
20
21
|
"@raclettejs/core/backend": ["./src/index.ts", "./src"],
|
|
21
22
|
"@raclettejs/core/backend/*": ["./src/*"],
|
|
22
23
|
"@raclettejs/core": ["../..", "/app/types"]
|