@raclettejs/workbench 0.0.1
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/README.md +1 -0
- package/config/compositions.js +282 -0
- package/config/interactionLinks.js +164 -0
- package/i18n/de-DE.json +245 -0
- package/i18n/en-EU.json +245 -0
- package/i18n/sk-SK.json +245 -0
- package/package.json +63 -0
- package/packages.json +33 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/CompositionConfiguration.vue +140 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/CompositionIntegration.vue +3 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/CompositionWidgetsLayout.vue +129 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetContextMenu.vue +53 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetIcon.vue +99 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetList.vue +122 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetRenderer.vue +65 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetSettings.vue +96 -0
- package/plugins/pacifico__compositions/frontend/widgets/CompositionCreateWidget.vue +51 -0
- package/plugins/pacifico__compositions/frontend/widgets/CompositionEditWidget.vue +66 -0
- package/plugins/pacifico__compositions/frontend/widgets/CompositionListWidget.vue +132 -0
- package/plugins/pacifico__compositions/raclette.plugin.ts +8 -0
- package/plugins/pacifico__core/backend/index.ts +25 -0
- package/plugins/pacifico__core/backend/routes.ts +220 -0
- package/plugins/pacifico__core/raclette.plugin.ts +9 -0
- package/plugins/pacifico__interactionLinks/frontend/components/InteractionLinkConfiguration.vue +145 -0
- package/plugins/pacifico__interactionLinks/frontend/widgets/InteractionLinkCreateWidget.vue +48 -0
- package/plugins/pacifico__interactionLinks/frontend/widgets/InteractionLinkEditWidget.vue +66 -0
- package/plugins/pacifico__interactionLinks/frontend/widgets/InteractionLinkListWidget.vue +126 -0
- package/plugins/pacifico__interactionLinks/raclette.plugin.ts +8 -0
- package/plugins/pacifico__plugins/frontend/index.ts +42 -0
- package/plugins/pacifico__plugins/frontend/widgets/PluginDetailWidget.vue +26 -0
- package/plugins/pacifico__plugins/frontend/widgets/PluginListWidget.vue +59 -0
- package/plugins/pacifico__plugins/raclette.plugin.ts +8 -0
- package/plugins/pacifico__tags/frontend/components/TagConfiguration.vue +99 -0
- package/plugins/pacifico__tags/frontend/widgets/TagCreateWidget.vue +43 -0
- package/plugins/pacifico__tags/frontend/widgets/TagEditWidget.vue +64 -0
- package/plugins/pacifico__tags/frontend/widgets/TagListWidget.vue +71 -0
- package/plugins/pacifico__tags/raclette.plugin.ts +8 -0
- package/plugins/pacifico__users/frontend/components/UserConfiguration.vue +110 -0
- package/plugins/pacifico__users/frontend/widgets/UserCreateWidget.vue +48 -0
- package/plugins/pacifico__users/frontend/widgets/UserEditWidget.vue +64 -0
- package/plugins/pacifico__users/frontend/widgets/UserListWidget.vue +86 -0
- package/plugins/pacifico__users/raclette.plugin.ts +8 -0
- package/raclette.config.js +150 -0
- package/services/frontend/.DS_Store +0 -0
- package/services/frontend/public/favicon.png +0 -0
- package/services/frontend/public/home-icon.png +0 -0
- package/services/frontend/public/logo.png +0 -0
- package/services/frontend/src/app/.DS_Store +0 -0
- package/services/frontend/src/app/components/BaseDataTable.vue +284 -0
- package/services/frontend/src/app/components/dynamicForm/DynamicForm.vue +207 -0
- package/services/frontend/src/app/components/dynamicForm/DynamicFormTypes.ts +63 -0
- package/services/frontend/src/app/components/dynamicForm/DynamicInput.vue +344 -0
- package/services/frontend/src/app/components/dynamicForm/TextDivider.vue +13 -0
- package/services/frontend/src/app/components/dynamicForm/testing/DynamicFormInputTesting.vue +477 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/CheckboxInput.vue +16 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/ColorInput.vue +52 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/CompositionInput.vue +32 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/IconInput.vue +40 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/ListInput.vue +51 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/LocalizedInput.vue +51 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/PasswordInput.vue +23 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/RecordInput.vue +91 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/SwitchInput.vue +17 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/TagsInput.vue +58 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/TriggerInput.vue +159 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/UserInput.vue +63 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/mdi-icons.ts +7002 -0
- package/services/frontend/src/app/components/dynamicForm/validators.ts +19 -0
- package/services/frontend/src/app/components/stepNavigator/StepNavigator.vue +331 -0
- package/services/frontend/src/app/components/stepNavigator/StepNavigatorTypes.ts +12 -0
- package/services/frontend/src/app/composables/useUserHelper.ts +254 -0
- package/services/frontend/src/app/composables/useVantaFog.ts +37 -0
- package/services/frontend/src/app/composables/useWorkbenchPlugins.ts +44 -0
- package/services/frontend/src/orchestrator/.DS_Store +0 -0
- package/services/frontend/src/orchestrator/LoginApp.vue +62 -0
- package/services/frontend/src/orchestrator/WelcomeScreen.vue +39 -0
- package/services/frontend/src/orchestrator/assets/styles/themes/dark.ts +39 -0
- package/services/frontend/src/orchestrator/assets/styles/themes/highContrast.ts +39 -0
- package/services/frontend/src/orchestrator/assets/styles/themes/light.ts +39 -0
- package/services/frontend/src/orchestrator/components/InitProjectScreen.vue +298 -0
- package/services/frontend/src/orchestrator/components/LoginScreen.vue +73 -0
- package/services/frontend/src/orchestrator/components/RacletteAdCorner.vue +313 -0
- package/services/frontend/src/orchestrator/components/layout/AppButton.vue +109 -0
- package/services/frontend/src/orchestrator/components/layout/AppHeadline.vue +46 -0
- package/services/frontend/src/orchestrator/components/layout/AppLabelledInput.vue +94 -0
- package/services/frontend/src/orchestrator/components/layout/AppModal.vue +28 -0
- package/services/frontend/src/orchestrator/components/layout/SvgButtonArrow.vue +14 -0
- package/services/frontend/src/orchestrator/composables/useWelcomeScreen.ts +19 -0
- package/yarn.lock +3435 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-list>
|
|
3
|
+
<v-list-subheader>
|
|
4
|
+
Settings for {{ selectedWigdetModel.name }}
|
|
5
|
+
</v-list-subheader>
|
|
6
|
+
|
|
7
|
+
<!-- NEW -->
|
|
8
|
+
<v-list-item v-for="(value, key) in configurableParameteters" :key="key">
|
|
9
|
+
<!-- text -->
|
|
10
|
+
<v-text-field
|
|
11
|
+
v-if="value.inputType === 'text'"
|
|
12
|
+
:model-value="getConfigValue(key)"
|
|
13
|
+
persistent-placeholder
|
|
14
|
+
:label="key"
|
|
15
|
+
@update:model-value="setConfigValue(key, $event)"
|
|
16
|
+
/>
|
|
17
|
+
|
|
18
|
+
<!-- checkbox -->
|
|
19
|
+
<v-checkbox
|
|
20
|
+
v-else-if="value.inputType === 'checkbox'"
|
|
21
|
+
:model-value="getConfigValue(key)"
|
|
22
|
+
:label="key"
|
|
23
|
+
@update:model-value="setConfigValue(key, $event)"
|
|
24
|
+
/>
|
|
25
|
+
|
|
26
|
+
<!-- switch -->
|
|
27
|
+
<v-switch
|
|
28
|
+
v-else-if="value.inputType === 'switch'"
|
|
29
|
+
:model-value="getConfigValue(key)"
|
|
30
|
+
:label="key"
|
|
31
|
+
@update:model-value="setConfigValue(key, $event)"
|
|
32
|
+
/>
|
|
33
|
+
|
|
34
|
+
<!-- number -->
|
|
35
|
+
<v-text-field
|
|
36
|
+
v-else-if="value.inputType === 'number'"
|
|
37
|
+
:model-value="getConfigValue(key)"
|
|
38
|
+
:label="key"
|
|
39
|
+
type="number"
|
|
40
|
+
:min="value.min"
|
|
41
|
+
:max="value.max"
|
|
42
|
+
:step="value.step"
|
|
43
|
+
@update:model-value="setConfigValue(key, parseFloat($event))"
|
|
44
|
+
/>
|
|
45
|
+
|
|
46
|
+
<!-- multiselect -->
|
|
47
|
+
<v-select
|
|
48
|
+
v-else-if="
|
|
49
|
+
value.inputType === 'multiselect' || value.inputType === 'select'
|
|
50
|
+
"
|
|
51
|
+
:label="key"
|
|
52
|
+
:items="value.selectionValues"
|
|
53
|
+
:model-value="getConfigValue(key)"
|
|
54
|
+
:multiple="value.inputType === 'multiselect'"
|
|
55
|
+
@update:model-value="setConfigValue(key, $event)"
|
|
56
|
+
/>
|
|
57
|
+
</v-list-item>
|
|
58
|
+
</v-list>
|
|
59
|
+
</template>
|
|
60
|
+
|
|
61
|
+
<script setup lang="ts">
|
|
62
|
+
import { computed } from "vue"
|
|
63
|
+
import useWidgets from "@raclettejs/core/orchestrator/composables/useWidgets"
|
|
64
|
+
import { WidgetBase } from "@raclettejs/core/frontend/store/types"
|
|
65
|
+
|
|
66
|
+
const selectedWigdetModel = defineModel<WidgetBase>({ required: true })
|
|
67
|
+
|
|
68
|
+
const { findWidgetByName } = useWidgets()
|
|
69
|
+
|
|
70
|
+
const widget = computed(() => findWidgetByName(selectedWigdetModel.value.name))
|
|
71
|
+
|
|
72
|
+
const configurableParameteters = computed(
|
|
73
|
+
() => widget.value?.configurableParameters,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
const getConfigValue = (key: string) => {
|
|
77
|
+
const config = selectedWigdetModel.value.config
|
|
78
|
+
|
|
79
|
+
if (config && key in config) {
|
|
80
|
+
return config[key]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const defaultValue = configurableParameteters.value![key].defaultValue
|
|
84
|
+
|
|
85
|
+
return defaultValue
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const setConfigValue = (key: string, value: any) => {
|
|
89
|
+
if (!selectedWigdetModel.value.config) {
|
|
90
|
+
selectedWigdetModel.value.config = {}
|
|
91
|
+
}
|
|
92
|
+
const config = selectedWigdetModel.value.config
|
|
93
|
+
|
|
94
|
+
config[key] = value
|
|
95
|
+
}
|
|
96
|
+
</script>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<CompositionConfiguration
|
|
3
|
+
v-model="newComposition"
|
|
4
|
+
:disabled="isLoading"
|
|
5
|
+
@save-and-finish="onSubmit(true)"
|
|
6
|
+
@save="onSubmit"
|
|
7
|
+
/>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import { CompositionCreate } from "@raclettejs/core"
|
|
12
|
+
import { ref } from "vue"
|
|
13
|
+
import CompositionConfiguration from "../components/compositionConfiguration/CompositionConfiguration.vue"
|
|
14
|
+
import {
|
|
15
|
+
usePluginApi,
|
|
16
|
+
useRouteState,
|
|
17
|
+
} from "@raclettejs/core/orchestrator/composables"
|
|
18
|
+
|
|
19
|
+
const props = defineProps<{
|
|
20
|
+
uuid: string
|
|
21
|
+
}>()
|
|
22
|
+
|
|
23
|
+
const { $data } = usePluginApi("raclette__core")
|
|
24
|
+
const { triggerInteractionLinkById } = useRouteState()
|
|
25
|
+
|
|
26
|
+
const { execute, isLoading, error } = $data.composition.create()
|
|
27
|
+
|
|
28
|
+
const newComposition = ref<CompositionCreate>({
|
|
29
|
+
title: "",
|
|
30
|
+
description: "",
|
|
31
|
+
tags: [],
|
|
32
|
+
pathname: {
|
|
33
|
+
default: "",
|
|
34
|
+
de: "",
|
|
35
|
+
en: "",
|
|
36
|
+
},
|
|
37
|
+
status: "inProgress",
|
|
38
|
+
widgetsLayout: [[]],
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const navigateBack = () =>
|
|
42
|
+
triggerInteractionLinkById("compositionListInteractionLink")
|
|
43
|
+
|
|
44
|
+
const onSubmit = async (finish = false) => {
|
|
45
|
+
await execute(newComposition.value)
|
|
46
|
+
|
|
47
|
+
if (!error.value && finish) {
|
|
48
|
+
navigateBack()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
</script>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<CompositionConfiguration
|
|
3
|
+
v-if="editableComposition"
|
|
4
|
+
v-model="editableComposition"
|
|
5
|
+
:disabled="isLoading"
|
|
6
|
+
is-editing
|
|
7
|
+
@save="onSubmit"
|
|
8
|
+
@save-and-finish="onSubmit(true)"
|
|
9
|
+
/>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup lang="ts">
|
|
13
|
+
import {
|
|
14
|
+
usePluginApi,
|
|
15
|
+
useRouteState,
|
|
16
|
+
} from "@raclettejs/core/orchestrator/composables"
|
|
17
|
+
import { computed, ref, toRaw } from "vue"
|
|
18
|
+
import CompositionConfiguration from "../components/compositionConfiguration/CompositionConfiguration.vue"
|
|
19
|
+
import { Composition } from "@raclettejs/core"
|
|
20
|
+
import { clone } from "ramda"
|
|
21
|
+
import { DEFAULT_SLOT_NAME } from "@raclettejs/core/orchestrator/router/routeParserHelper"
|
|
22
|
+
|
|
23
|
+
const props = defineProps<{
|
|
24
|
+
uuid: string
|
|
25
|
+
}>()
|
|
26
|
+
|
|
27
|
+
const { compositionSlots, triggerInteractionLinkById } = useRouteState()
|
|
28
|
+
const { $data } = usePluginApi("raclette__core")
|
|
29
|
+
|
|
30
|
+
const id = computed(
|
|
31
|
+
() =>
|
|
32
|
+
compositionSlots.value.find((slot) => slot.slotName === DEFAULT_SLOT_NAME)
|
|
33
|
+
?.slotParams?.id,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const { data } = $data.composition.get({
|
|
37
|
+
params: { _id: id.value },
|
|
38
|
+
options: {
|
|
39
|
+
immediate: true,
|
|
40
|
+
cb: () => {
|
|
41
|
+
if (!composition.value) {
|
|
42
|
+
return navigateBack()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
editableComposition.value = clone(composition.value)
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const { execute: executeUpdate, isLoading } = $data.composition.update()
|
|
51
|
+
const editableComposition = ref<Composition>()
|
|
52
|
+
|
|
53
|
+
const composition = computed<Composition | undefined>(() => {
|
|
54
|
+
if (!id.value) {
|
|
55
|
+
return undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return data.value?.[id.value]
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const navigateBack = () =>
|
|
62
|
+
triggerInteractionLinkById("compositionListInteractionLink")
|
|
63
|
+
|
|
64
|
+
const onSubmit = async (finish = false) =>
|
|
65
|
+
await executeUpdate(toRaw(editableComposition.value))
|
|
66
|
+
</script>
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<BaseDataTable
|
|
3
|
+
:items="parsedCompositions"
|
|
4
|
+
:loading="compositionsLoading || tagsLoading"
|
|
5
|
+
:headers="headers"
|
|
6
|
+
create-interaction-link-id="compositionCreateInteractionLink"
|
|
7
|
+
edit-interaction-link-id="compositionEditInteractionLink"
|
|
8
|
+
:data-name="$t('workbench.compositionList.dataName')"
|
|
9
|
+
:data-article="$t('workbench.compositionList.dataArticle')"
|
|
10
|
+
@delete="deleteComposition"
|
|
11
|
+
>
|
|
12
|
+
<!-- Custom status column -->
|
|
13
|
+
<template #item.status="{ item }">
|
|
14
|
+
<v-icon
|
|
15
|
+
:icon="item.statusIcon"
|
|
16
|
+
:color="item.statusColor"
|
|
17
|
+
size="x-large"
|
|
18
|
+
/>
|
|
19
|
+
</template>
|
|
20
|
+
</BaseDataTable>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script setup lang="ts">
|
|
24
|
+
import { usePluginApi } from "@raclettejs/core/orchestrator/composables"
|
|
25
|
+
import { computed } from "vue"
|
|
26
|
+
import type { Composition } from "@raclettejs/core"
|
|
27
|
+
import { formatDistance } from "date-fns"
|
|
28
|
+
import BaseDataTable from "@app/components/BaseDataTable.vue"
|
|
29
|
+
import { useI18n } from "vue-i18n"
|
|
30
|
+
|
|
31
|
+
const { t } = useI18n()
|
|
32
|
+
|
|
33
|
+
const headers = computed<
|
|
34
|
+
{ title: string; key: keyof Composition | "actions" }[]
|
|
35
|
+
>(() => [
|
|
36
|
+
{
|
|
37
|
+
title: t("workbench.compositionList.tableHeaders.title"),
|
|
38
|
+
key: "title",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
title: t("workbench.compositionList.tableHeaders.status"),
|
|
42
|
+
key: "status",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
title: t("workbench.compositionList.tableHeaders.tags"),
|
|
46
|
+
key: "tags",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
title: t("workbench.compositionList.tableHeaders.lastEditor"),
|
|
50
|
+
key: "lastEditor",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
title: t("workbench.compositionList.tableHeaders.author"),
|
|
54
|
+
key: "author",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
title: t("workbench.compositionList.tableHeaders.updatedAt"),
|
|
58
|
+
key: "updatedAt",
|
|
59
|
+
},
|
|
60
|
+
])
|
|
61
|
+
|
|
62
|
+
const props = defineProps<{
|
|
63
|
+
uuid: string
|
|
64
|
+
}>()
|
|
65
|
+
|
|
66
|
+
const { $data, $socket } = usePluginApi("raclette__core")
|
|
67
|
+
const {
|
|
68
|
+
data: compositions,
|
|
69
|
+
execute,
|
|
70
|
+
isLoading: compositionsLoading,
|
|
71
|
+
} = $data.composition.getAll({
|
|
72
|
+
options: { immediate: true },
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const { data: tags, isLoading: tagsLoading } = $data.tag.getAll({
|
|
76
|
+
options: { immediate: true },
|
|
77
|
+
})
|
|
78
|
+
const { data: users } = $data.user.getAll({
|
|
79
|
+
options: { immediate: true },
|
|
80
|
+
})
|
|
81
|
+
const { execute: executeDelete } = await $data.composition.delete({
|
|
82
|
+
options: { cb: () => execute() },
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const parsedCompositions = computed(() => {
|
|
86
|
+
if (compositions.value && tags.value) {
|
|
87
|
+
return Object.values(
|
|
88
|
+
compositions.value as { [key: string]: Composition },
|
|
89
|
+
).map((composition) => ({
|
|
90
|
+
...composition,
|
|
91
|
+
tags: composition.tags.map((tag) => tags.value?.[tag]),
|
|
92
|
+
lastEditor: users.value?.[composition.author]?.email,
|
|
93
|
+
author: users.value?.[composition.author]?.email,
|
|
94
|
+
status: composition.status, // TODO: parse status
|
|
95
|
+
statusIcon: getStatusIcon(composition.status),
|
|
96
|
+
statusColor: getStatusColor(composition.status),
|
|
97
|
+
createdAt: formatDistance(new Date(composition.createdAt), new Date()),
|
|
98
|
+
updatedAt: formatDistance(new Date(composition.updatedAt), new Date()),
|
|
99
|
+
}))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return []
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// TODO: replace with composition status type
|
|
106
|
+
const getStatusIcon = (status: string) => {
|
|
107
|
+
// if (status === "published") {
|
|
108
|
+
if (status === "inProgress") {
|
|
109
|
+
return "mdi-check-circle-outline"
|
|
110
|
+
} else if (status === "inProgress") {
|
|
111
|
+
return "mdi-pencil-circle-outline"
|
|
112
|
+
} else if (status === "removed") {
|
|
113
|
+
return "mdi-alert-circle-outline"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const getStatusColor = (status: string) => {
|
|
118
|
+
// if (status === "published") {
|
|
119
|
+
if (status === "inProgress") {
|
|
120
|
+
return "#19A038"
|
|
121
|
+
} else if (status === "inProgress") {
|
|
122
|
+
return "#898989"
|
|
123
|
+
} else if (status === "removed") {
|
|
124
|
+
return "#B66218"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const deleteComposition = async (composition: Composition) =>
|
|
129
|
+
await executeDelete({ _id: composition._id })
|
|
130
|
+
|
|
131
|
+
$socket.on("compositionCreated", () => execute())
|
|
132
|
+
</script>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PluginOptions,
|
|
3
|
+
PluginFastifyInstance,
|
|
4
|
+
} from "@raclettejs/core/backend" // this import is needed for TS support
|
|
5
|
+
import { registerRoutes } from "./routes"
|
|
6
|
+
|
|
7
|
+
const corePlugin = async (
|
|
8
|
+
fastify: PluginFastifyInstance,
|
|
9
|
+
_opts: PluginOptions,
|
|
10
|
+
) => {
|
|
11
|
+
/*
|
|
12
|
+
* ---------------------------------------------------------------------
|
|
13
|
+
* REGISTER ALL YOUR ROUTES
|
|
14
|
+
* ---------------------------------------------------------------------
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
await fastify.register((instance) => registerRoutes(instance))
|
|
19
|
+
} catch (error) {
|
|
20
|
+
fastify.log.error("Failed to register routes.", error)
|
|
21
|
+
throw error // Let the application handle the error
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default corePlugin
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { callDataTypeOperation } from "@raclettejs/core/backend"
|
|
2
|
+
import type { PluginFastifyInstance } from "@raclettejs/core"
|
|
3
|
+
import { Type } from "@sinclair/typebox"
|
|
4
|
+
import type { Static } from "@sinclair/typebox"
|
|
5
|
+
import type { FastifyRequest, FastifyReply } from "fastify"
|
|
6
|
+
|
|
7
|
+
const BodySchema = Type.Object({
|
|
8
|
+
title: Type.String(),
|
|
9
|
+
user: Type.Object({
|
|
10
|
+
email: Type.String(),
|
|
11
|
+
password: Type.String(),
|
|
12
|
+
firstname: Type.String(),
|
|
13
|
+
lastname: Type.String(),
|
|
14
|
+
}),
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
type Body = Static<typeof BodySchema>
|
|
18
|
+
|
|
19
|
+
export const registerRoutes = async (fastify: PluginFastifyInstance) => {
|
|
20
|
+
fastify.post(
|
|
21
|
+
"/init-project",
|
|
22
|
+
{ schema: { body: BodySchema } },
|
|
23
|
+
async (
|
|
24
|
+
req: FastifyRequest<{
|
|
25
|
+
Body: Body
|
|
26
|
+
}>,
|
|
27
|
+
reply: FastifyReply,
|
|
28
|
+
) => {
|
|
29
|
+
const created = {}
|
|
30
|
+
try {
|
|
31
|
+
// Check if setup is already completed
|
|
32
|
+
const existingProjects = await callDataTypeOperation(
|
|
33
|
+
"raclette__core",
|
|
34
|
+
"project",
|
|
35
|
+
"readAll",
|
|
36
|
+
fastify,
|
|
37
|
+
)
|
|
38
|
+
const existingAccounts = await callDataTypeOperation(
|
|
39
|
+
"raclette__core",
|
|
40
|
+
"account",
|
|
41
|
+
"readAll",
|
|
42
|
+
fastify,
|
|
43
|
+
)
|
|
44
|
+
const existingUsers = await callDataTypeOperation(
|
|
45
|
+
"raclette__core",
|
|
46
|
+
"user",
|
|
47
|
+
"readAll",
|
|
48
|
+
fastify,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
// If everything exists, lock the endpoint
|
|
52
|
+
if (
|
|
53
|
+
existingProjects.length > 0 &&
|
|
54
|
+
existingAccounts.length > 0 &&
|
|
55
|
+
existingUsers.length > 0
|
|
56
|
+
) {
|
|
57
|
+
return reply.status(403).send({
|
|
58
|
+
error: "Setup already completed. Init-project endpoint is locked.",
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let account
|
|
63
|
+
let adminTag
|
|
64
|
+
let user
|
|
65
|
+
let project
|
|
66
|
+
let projectConfig
|
|
67
|
+
|
|
68
|
+
// Step 1: Create or retrieve account
|
|
69
|
+
if (existingAccounts.length === 0) {
|
|
70
|
+
account = await callDataTypeOperation(
|
|
71
|
+
"raclette__core",
|
|
72
|
+
"account",
|
|
73
|
+
"create",
|
|
74
|
+
fastify,
|
|
75
|
+
{ name: "DEFAULT" },
|
|
76
|
+
"SYSTEM",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
fastify.log.info("Created new account")
|
|
80
|
+
} else {
|
|
81
|
+
account = existingAccounts[0]
|
|
82
|
+
fastify.log.info("Using existing account", account._id)
|
|
83
|
+
}
|
|
84
|
+
created.account = account
|
|
85
|
+
|
|
86
|
+
// Step 2: Create or retrieve admin tag
|
|
87
|
+
const existingTags = await callDataTypeOperation(
|
|
88
|
+
"raclette__core",
|
|
89
|
+
"tag",
|
|
90
|
+
"readAll",
|
|
91
|
+
fastify,
|
|
92
|
+
{
|
|
93
|
+
title: "ADMIN",
|
|
94
|
+
type: "system",
|
|
95
|
+
},
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if (existingTags.length === 0) {
|
|
99
|
+
adminTag = await callDataTypeOperation(
|
|
100
|
+
"raclette__core",
|
|
101
|
+
"tag",
|
|
102
|
+
"create",
|
|
103
|
+
fastify,
|
|
104
|
+
{
|
|
105
|
+
title: "ADMIN",
|
|
106
|
+
color: "#FF0000",
|
|
107
|
+
type: "system",
|
|
108
|
+
locked: true,
|
|
109
|
+
},
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
fastify.log.info("Created admin tag")
|
|
113
|
+
} else {
|
|
114
|
+
adminTag = existingTags[0]
|
|
115
|
+
fastify.log.info("Using existing admin tag", adminTag._id)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Step 3: Create or retrieve user
|
|
119
|
+
const existingUser = await callDataTypeOperation(
|
|
120
|
+
"raclette__core",
|
|
121
|
+
"user",
|
|
122
|
+
"readByMail",
|
|
123
|
+
req.body.user?.email,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if (!existingUser) {
|
|
127
|
+
user = await callDataTypeOperation(
|
|
128
|
+
"raclette__core",
|
|
129
|
+
"user",
|
|
130
|
+
"create",
|
|
131
|
+
fastify,
|
|
132
|
+
{
|
|
133
|
+
email: req.body.user?.email,
|
|
134
|
+
account: account._id,
|
|
135
|
+
password: req.body.user?.password,
|
|
136
|
+
email_verified: true,
|
|
137
|
+
tags: [adminTag._id],
|
|
138
|
+
firstname: req.body.user?.firstname,
|
|
139
|
+
lastname: req.body.user?.lastname,
|
|
140
|
+
},
|
|
141
|
+
)
|
|
142
|
+
fastify.log.info("Created new user")
|
|
143
|
+
} else {
|
|
144
|
+
user = existingUser[0]
|
|
145
|
+
fastify.log.info("Using existing user", user._id)
|
|
146
|
+
}
|
|
147
|
+
created.user = user
|
|
148
|
+
|
|
149
|
+
// Step 4: Update account with owner if not already set
|
|
150
|
+
if (!account.owner || account.owner === "SYSTEM") {
|
|
151
|
+
const updatedAccount = await callDataTypeOperation(
|
|
152
|
+
"raclette__core",
|
|
153
|
+
"account",
|
|
154
|
+
"update",
|
|
155
|
+
fastify,
|
|
156
|
+
account._id,
|
|
157
|
+
{
|
|
158
|
+
owner: user._id,
|
|
159
|
+
},
|
|
160
|
+
user._id,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
account = updatedAccount.updated
|
|
164
|
+
fastify.log.info("Updated account with owner")
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Step 5: Create or retrieve project
|
|
168
|
+
const existingProject = await callDataTypeOperation(
|
|
169
|
+
"raclette__core",
|
|
170
|
+
"project",
|
|
171
|
+
"readAll",
|
|
172
|
+
fastify,
|
|
173
|
+
{
|
|
174
|
+
account: account._id,
|
|
175
|
+
isDefault: true,
|
|
176
|
+
},
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if (existingProject.length === 0) {
|
|
180
|
+
project = await callDataTypeOperation(
|
|
181
|
+
"raclette__core",
|
|
182
|
+
"project",
|
|
183
|
+
"create",
|
|
184
|
+
fastify,
|
|
185
|
+
{
|
|
186
|
+
title: req.body.title,
|
|
187
|
+
account: account._id,
|
|
188
|
+
isDefault: true,
|
|
189
|
+
},
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
fastify.log.info("Created new project")
|
|
193
|
+
} else {
|
|
194
|
+
project = existingProject[0]
|
|
195
|
+
fastify.log.info("Using existing project", project._id)
|
|
196
|
+
}
|
|
197
|
+
created.project = project
|
|
198
|
+
|
|
199
|
+
const payload = {
|
|
200
|
+
success: true,
|
|
201
|
+
partial: false,
|
|
202
|
+
created,
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
reply.status(200).send(payload)
|
|
206
|
+
} catch (error) {
|
|
207
|
+
fastify.log.error(
|
|
208
|
+
"Error during init-project call. Process partially completed.",
|
|
209
|
+
error,
|
|
210
|
+
)
|
|
211
|
+
reply.status(500).send({
|
|
212
|
+
error:
|
|
213
|
+
"Error during init-project call. Process partially completed. You can retry to continue setup.",
|
|
214
|
+
details: error instanceof Error ? error.message : String(error),
|
|
215
|
+
created,
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
)
|
|
220
|
+
}
|