@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.
Files changed (89) hide show
  1. package/README.md +1 -0
  2. package/config/compositions.js +282 -0
  3. package/config/interactionLinks.js +164 -0
  4. package/i18n/de-DE.json +245 -0
  5. package/i18n/en-EU.json +245 -0
  6. package/i18n/sk-SK.json +245 -0
  7. package/package.json +63 -0
  8. package/packages.json +33 -0
  9. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/CompositionConfiguration.vue +140 -0
  10. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/CompositionIntegration.vue +3 -0
  11. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/CompositionWidgetsLayout.vue +129 -0
  12. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetContextMenu.vue +53 -0
  13. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetIcon.vue +99 -0
  14. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetList.vue +122 -0
  15. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetRenderer.vue +65 -0
  16. package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetSettings.vue +96 -0
  17. package/plugins/pacifico__compositions/frontend/widgets/CompositionCreateWidget.vue +51 -0
  18. package/plugins/pacifico__compositions/frontend/widgets/CompositionEditWidget.vue +66 -0
  19. package/plugins/pacifico__compositions/frontend/widgets/CompositionListWidget.vue +132 -0
  20. package/plugins/pacifico__compositions/raclette.plugin.ts +8 -0
  21. package/plugins/pacifico__core/backend/index.ts +25 -0
  22. package/plugins/pacifico__core/backend/routes.ts +220 -0
  23. package/plugins/pacifico__core/raclette.plugin.ts +9 -0
  24. package/plugins/pacifico__interactionLinks/frontend/components/InteractionLinkConfiguration.vue +145 -0
  25. package/plugins/pacifico__interactionLinks/frontend/widgets/InteractionLinkCreateWidget.vue +48 -0
  26. package/plugins/pacifico__interactionLinks/frontend/widgets/InteractionLinkEditWidget.vue +66 -0
  27. package/plugins/pacifico__interactionLinks/frontend/widgets/InteractionLinkListWidget.vue +126 -0
  28. package/plugins/pacifico__interactionLinks/raclette.plugin.ts +8 -0
  29. package/plugins/pacifico__plugins/frontend/index.ts +42 -0
  30. package/plugins/pacifico__plugins/frontend/widgets/PluginDetailWidget.vue +26 -0
  31. package/plugins/pacifico__plugins/frontend/widgets/PluginListWidget.vue +59 -0
  32. package/plugins/pacifico__plugins/raclette.plugin.ts +8 -0
  33. package/plugins/pacifico__tags/frontend/components/TagConfiguration.vue +99 -0
  34. package/plugins/pacifico__tags/frontend/widgets/TagCreateWidget.vue +43 -0
  35. package/plugins/pacifico__tags/frontend/widgets/TagEditWidget.vue +64 -0
  36. package/plugins/pacifico__tags/frontend/widgets/TagListWidget.vue +71 -0
  37. package/plugins/pacifico__tags/raclette.plugin.ts +8 -0
  38. package/plugins/pacifico__users/frontend/components/UserConfiguration.vue +110 -0
  39. package/plugins/pacifico__users/frontend/widgets/UserCreateWidget.vue +48 -0
  40. package/plugins/pacifico__users/frontend/widgets/UserEditWidget.vue +64 -0
  41. package/plugins/pacifico__users/frontend/widgets/UserListWidget.vue +86 -0
  42. package/plugins/pacifico__users/raclette.plugin.ts +8 -0
  43. package/raclette.config.js +150 -0
  44. package/services/frontend/.DS_Store +0 -0
  45. package/services/frontend/public/favicon.png +0 -0
  46. package/services/frontend/public/home-icon.png +0 -0
  47. package/services/frontend/public/logo.png +0 -0
  48. package/services/frontend/src/app/.DS_Store +0 -0
  49. package/services/frontend/src/app/components/BaseDataTable.vue +284 -0
  50. package/services/frontend/src/app/components/dynamicForm/DynamicForm.vue +207 -0
  51. package/services/frontend/src/app/components/dynamicForm/DynamicFormTypes.ts +63 -0
  52. package/services/frontend/src/app/components/dynamicForm/DynamicInput.vue +344 -0
  53. package/services/frontend/src/app/components/dynamicForm/TextDivider.vue +13 -0
  54. package/services/frontend/src/app/components/dynamicForm/testing/DynamicFormInputTesting.vue +477 -0
  55. package/services/frontend/src/app/components/dynamicForm/typedInputs/CheckboxInput.vue +16 -0
  56. package/services/frontend/src/app/components/dynamicForm/typedInputs/ColorInput.vue +52 -0
  57. package/services/frontend/src/app/components/dynamicForm/typedInputs/CompositionInput.vue +32 -0
  58. package/services/frontend/src/app/components/dynamicForm/typedInputs/IconInput.vue +40 -0
  59. package/services/frontend/src/app/components/dynamicForm/typedInputs/ListInput.vue +51 -0
  60. package/services/frontend/src/app/components/dynamicForm/typedInputs/LocalizedInput.vue +51 -0
  61. package/services/frontend/src/app/components/dynamicForm/typedInputs/PasswordInput.vue +23 -0
  62. package/services/frontend/src/app/components/dynamicForm/typedInputs/RecordInput.vue +91 -0
  63. package/services/frontend/src/app/components/dynamicForm/typedInputs/SwitchInput.vue +17 -0
  64. package/services/frontend/src/app/components/dynamicForm/typedInputs/TagsInput.vue +58 -0
  65. package/services/frontend/src/app/components/dynamicForm/typedInputs/TriggerInput.vue +159 -0
  66. package/services/frontend/src/app/components/dynamicForm/typedInputs/UserInput.vue +63 -0
  67. package/services/frontend/src/app/components/dynamicForm/typedInputs/mdi-icons.ts +7002 -0
  68. package/services/frontend/src/app/components/dynamicForm/validators.ts +19 -0
  69. package/services/frontend/src/app/components/stepNavigator/StepNavigator.vue +331 -0
  70. package/services/frontend/src/app/components/stepNavigator/StepNavigatorTypes.ts +12 -0
  71. package/services/frontend/src/app/composables/useUserHelper.ts +254 -0
  72. package/services/frontend/src/app/composables/useVantaFog.ts +37 -0
  73. package/services/frontend/src/app/composables/useWorkbenchPlugins.ts +44 -0
  74. package/services/frontend/src/orchestrator/.DS_Store +0 -0
  75. package/services/frontend/src/orchestrator/LoginApp.vue +62 -0
  76. package/services/frontend/src/orchestrator/WelcomeScreen.vue +39 -0
  77. package/services/frontend/src/orchestrator/assets/styles/themes/dark.ts +39 -0
  78. package/services/frontend/src/orchestrator/assets/styles/themes/highContrast.ts +39 -0
  79. package/services/frontend/src/orchestrator/assets/styles/themes/light.ts +39 -0
  80. package/services/frontend/src/orchestrator/components/InitProjectScreen.vue +298 -0
  81. package/services/frontend/src/orchestrator/components/LoginScreen.vue +73 -0
  82. package/services/frontend/src/orchestrator/components/RacletteAdCorner.vue +313 -0
  83. package/services/frontend/src/orchestrator/components/layout/AppButton.vue +109 -0
  84. package/services/frontend/src/orchestrator/components/layout/AppHeadline.vue +46 -0
  85. package/services/frontend/src/orchestrator/components/layout/AppLabelledInput.vue +94 -0
  86. package/services/frontend/src/orchestrator/components/layout/AppModal.vue +28 -0
  87. package/services/frontend/src/orchestrator/components/layout/SvgButtonArrow.vue +14 -0
  88. package/services/frontend/src/orchestrator/composables/useWelcomeScreen.ts +19 -0
  89. 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,8 @@
1
+ export default {
2
+ name: "compositions",
3
+ author: "pacifico",
4
+ version: "0.0.1",
5
+ description: "A plugin for compositions",
6
+ // Specify where the main frontend and backend entry points are
7
+ frontendDir: "./frontend",
8
+ }
@@ -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
+ }
@@ -0,0 +1,9 @@
1
+ export default {
2
+ name: "core",
3
+ author: "pacifico",
4
+ version: "0.0.1",
5
+ description: "A plugin for core dataTypes",
6
+ // Specify where the main frontend and backend entry points are
7
+ backendDir: "./backend",
8
+ frontendDir: "./frontend",
9
+ }