@geode/opengeodeweb-front 10.5.1 → 10.6.0

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 (41) hide show
  1. package/app/components/FileUploader.vue +4 -5
  2. package/app/stores/app.js +80 -3
  3. package/app/stores/geode.js +54 -7
  4. package/app/stores/infra.js +25 -14
  5. package/app/stores/lambda.js +2 -1
  6. package/app/stores/viewer.js +40 -11
  7. package/app/utils/config.js +45 -0
  8. package/app/utils/extension.js +109 -0
  9. package/app/utils/local/microservices.js +259 -0
  10. package/app/utils/local/path.js +86 -0
  11. package/app/utils/local/scripts.js +49 -0
  12. package/app/utils/server.js +45 -0
  13. package/{app → internal}/utils/upload_file.js +8 -8
  14. package/nuxt.config.js +22 -4
  15. package/package.json +9 -4
  16. package/server/api/app/kill.post.js +11 -0
  17. package/server/api/app/project_folder_path.post.js +29 -0
  18. package/server/api/app/run_back.post.js +33 -0
  19. package/server/api/app/run_viewer.post.js +33 -0
  20. package/server/api/extensions/run.post.js +98 -0
  21. package/server/api/extensions/upload.put.js +105 -0
  22. package/tests/integration/microservices/back/requirements.txt +1 -1
  23. package/tests/integration/setup.js +39 -45
  24. package/tests/integration/stores/data_style/mesh/cells.nuxt.test.js +11 -22
  25. package/tests/integration/stores/data_style/mesh/edges.nuxt.test.js +10 -20
  26. package/tests/integration/stores/data_style/mesh/index.nuxt.test.js +10 -20
  27. package/tests/integration/stores/data_style/mesh/points.nuxt.test.js +10 -20
  28. package/tests/integration/stores/data_style/mesh/polygons.nuxt.test.js +10 -21
  29. package/tests/integration/stores/data_style/mesh/polyhedra.nuxt.test.js +10 -20
  30. package/tests/integration/stores/data_style/model/blocks.nuxt.test.js +10 -20
  31. package/tests/integration/stores/data_style/model/corners.nuxt.test.js +10 -20
  32. package/tests/integration/stores/data_style/model/edges.nuxt.test.js +10 -20
  33. package/tests/integration/stores/data_style/model/index.nuxt.test.js +10 -20
  34. package/tests/integration/stores/data_style/model/lines.nuxt.test.js +10 -16
  35. package/tests/integration/stores/data_style/model/points.nuxt.test.js +10 -15
  36. package/tests/integration/stores/data_style/model/surfaces.nuxt.test.js +15 -26
  37. package/tests/integration/stores/viewer.nuxt.test.js +6 -10
  38. package/tests/unit/composables/upload_file.nuxt.test.js +7 -9
  39. package/tests/unit/stores/infra.nuxt.test.js +6 -6
  40. package/tests/unit/stores/lambda.nuxt.test.js +2 -2
  41. package/app/utils/local.js +0 -361
@@ -1,9 +1,6 @@
1
1
  <script setup>
2
2
  import DragAndDrop from "@ogw_front/components/DragAndDrop"
3
- import schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json"
4
- import { upload_file } from "@ogw_front/utils/upload_file"
5
-
6
- const schema = schemas.opengeodeweb_back.upload_file
3
+ import { useGeodeStore } from "@ogw_front/stores/geode"
7
4
 
8
5
  const emit = defineEmits(["files_uploaded", "decrement_step", "reset_values"])
9
6
 
@@ -15,6 +12,8 @@
15
12
  mini: { type: Boolean, required: false, default: false },
16
13
  })
17
14
 
15
+ const geodeStore = useGeodeStore()
16
+
18
17
  const internal_files = ref(files)
19
18
  const loading = ref(false)
20
19
  const files_uploaded = ref(false)
@@ -40,7 +39,7 @@
40
39
  async function upload_files() {
41
40
  toggle_loading()
42
41
  const promise_array = internal_files.value.map((file) =>
43
- upload_file({ route: schema.$id, file }),
42
+ geodeStore.upload(file),
44
43
  )
45
44
  await Promise.all(promise_array)
46
45
  files_uploaded.value = true
package/app/stores/app.js CHANGED
@@ -1,3 +1,6 @@
1
+ import { upload_file } from "@ogw_internal/utils/upload_file.js"
2
+ import { api_fetch } from "@ogw_internal/utils/api_fetch.js"
3
+
1
4
  export const useAppStore = defineStore("app", () => {
2
5
  const stores = []
3
6
 
@@ -108,8 +111,7 @@ export const useAppStore = defineStore("app", () => {
108
111
  })
109
112
  finalURL = URL.createObjectURL(newBlob)
110
113
  }
111
-
112
- const extensionModule = await import(finalURL)
114
+ const extensionModule = await import(/* @vite-ignore */ finalURL)
113
115
 
114
116
  if (finalURL !== path && finalURL.startsWith("blob:")) {
115
117
  URL.revokeObjectURL(finalURL)
@@ -145,7 +147,6 @@ export const useAppStore = defineStore("app", () => {
145
147
  loadedExtensions.value.set(extensionId, extensionData)
146
148
 
147
149
  console.log(`[AppStore] Extension loaded successfully: ${extensionId}`)
148
-
149
150
  return extensionModule
150
151
  } else {
151
152
  throw new Error("Extension must export an install function")
@@ -217,6 +218,76 @@ export const useAppStore = defineStore("app", () => {
217
218
  return getExtension(id)?.enabled ?? false
218
219
  }
219
220
 
221
+ function upload(file, callbacks = {}) {
222
+ const route = "/api/extensions/upload"
223
+ const store = useAppStore()
224
+ return upload_file(
225
+ store,
226
+ { route, file },
227
+ {
228
+ ...callbacks,
229
+ response_function: async (response) => {
230
+ console.log("[APP] Request completed:", route)
231
+ if (callbacks.response_function) {
232
+ await callbacks.response_function(response)
233
+ }
234
+ },
235
+ },
236
+ )
237
+ }
238
+
239
+ function request(schema, params, callbacks = {}) {
240
+ console.log("[APP] Request:", schema.$id)
241
+
242
+ const store = useAppStore()
243
+ return api_fetch(
244
+ store,
245
+ { schema, params },
246
+ {
247
+ ...callbacks,
248
+ response_function: async (response) => {
249
+ console.log("[APP] Request completed:", schema.$id)
250
+ if (callbacks.response_function) {
251
+ await callbacks.response_function(response)
252
+ }
253
+ },
254
+ },
255
+ )
256
+ }
257
+
258
+ const request_counter = ref(0)
259
+ function start_request() {
260
+ request_counter.value += 1
261
+ }
262
+ function stop_request() {
263
+ request_counter.value -= 1
264
+ }
265
+
266
+ const projectFolderPath = ref("")
267
+ function createProjectFolder() {
268
+ const { PROJECT } = useRuntimeConfig().public
269
+ const schema = {
270
+ $id: "/api/app/project_folder_path",
271
+ methods: ["POST"],
272
+ type: "object",
273
+ properties: {
274
+ PROJECT: { type: "string" },
275
+ },
276
+ required: ["PROJECT"],
277
+ additionalProperties: true,
278
+ }
279
+ const params = { PROJECT }
280
+
281
+ console.log(createProjectFolder.name, { PROJECT })
282
+
283
+ return request(schema, params, {
284
+ response_function: async (response) => {
285
+ console.log(`[APP] ${response.projectFolderPath} created`)
286
+ projectFolderPath.value = response.projectFolderPath
287
+ },
288
+ })
289
+ }
290
+
220
291
  return {
221
292
  stores,
222
293
  registerStore,
@@ -233,5 +304,11 @@ export const useAppStore = defineStore("app", () => {
233
304
  toggleExtension,
234
305
  setExtensionEnabled,
235
306
  getExtensionEnabled,
307
+ request,
308
+ upload,
309
+ projectFolderPath,
310
+ createProjectFolder,
311
+ start_request,
312
+ stop_request,
236
313
  }
237
314
  })
@@ -4,6 +4,8 @@ import { appMode } from "@ogw_front/utils/app_mode"
4
4
  import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json"
5
5
  import { useFeedbackStore } from "@ogw_front/stores/feedback"
6
6
  import { useInfraStore } from "@ogw_front/stores/infra"
7
+ import { useAppStore } from "@ogw_front/stores/app"
8
+ import { upload_file } from "@ogw_internal/utils/upload_file.js"
7
9
 
8
10
  const MILLISECONDS_IN_SECOND = 1000
9
11
  const DEFAULT_PING_INTERVAL_SECONDS = 10
@@ -25,10 +27,6 @@ export const useGeodeStore = defineStore("geode", {
25
27
  if (useInfraStore().app_mode === appMode.CLOUD) {
26
28
  return "443"
27
29
  }
28
- const { GEODE_PORT } = useRuntimeConfig().public
29
- if (GEODE_PORT !== null && GEODE_PORT !== "") {
30
- return GEODE_PORT
31
- }
32
30
  return this.default_local_port
33
31
  },
34
32
  base_url() {
@@ -81,9 +79,39 @@ export const useGeodeStore = defineStore("geode", {
81
79
  stop_request() {
82
80
  this.request_counter -= 1
83
81
  },
84
- launch() {
85
- console.log("[GEODE] Launching geode microservice...")
86
- return globalThis.electronAPI.run_back()
82
+ launch(args) {
83
+ console.log("[GEODE] Launching back microservice...", { args })
84
+ const appStore = useAppStore()
85
+
86
+ const { BACK_PATH, BACK_COMMAND } = useRuntimeConfig().public
87
+
88
+ console.log("[GEODE] BACK_PATH", BACK_PATH)
89
+ console.log("[GEODE] BACK_COMMAND", BACK_COMMAND)
90
+ const schema = {
91
+ $id: "/api/app/run_back",
92
+ methods: ["POST"],
93
+ type: "object",
94
+ properties: {
95
+ BACK_PATH: { type: "string" },
96
+ BACK_COMMAND: { type: "string" },
97
+ },
98
+ required: ["BACK_PATH", "BACK_COMMAND"],
99
+ additionalProperties: true,
100
+ }
101
+
102
+ const params = {
103
+ BACK_PATH,
104
+ BACK_COMMAND,
105
+ args,
106
+ }
107
+
108
+ console.log("[GEODE] params", params)
109
+ return appStore.request(schema, params, {
110
+ response_function: (response) => {
111
+ console.log(`[GEODE] Back launched on port ${response.port}`)
112
+ this.default_local_port = response.port
113
+ },
114
+ })
87
115
  },
88
116
  connect() {
89
117
  console.log("[GEODE] Connecting to geode microservice...")
@@ -114,6 +142,25 @@ export const useGeodeStore = defineStore("geode", {
114
142
  },
115
143
  )
116
144
  },
145
+ upload(file, callbacks = {}) {
146
+ const route = back_schemas.opengeodeweb_back.upload_file.$id
147
+ return upload_file(
148
+ this,
149
+ {
150
+ route,
151
+ file,
152
+ },
153
+ {
154
+ ...callbacks,
155
+ response_function: async (response) => {
156
+ console.log("[GEODE] Request completed:", route)
157
+ if (callbacks.response_function) {
158
+ await callbacks.response_function(response)
159
+ }
160
+ },
161
+ },
162
+ )
163
+ },
117
164
  },
118
165
  share: {
119
166
  omit: ["status"],
@@ -1,6 +1,9 @@
1
1
  import { appMode, getAppMode } from "@ogw_front/utils/app_mode"
2
2
  import { Status } from "@ogw_front/utils/status"
3
3
  import { useLambdaStore } from "@ogw_front/stores/lambda"
4
+ import { useAppStore } from "@ogw_front/stores/app"
5
+
6
+ import { registerRunningExtensions } from "@ogw_front/utils/extension"
4
7
 
5
8
  export const useInfraStore = defineStore("infra", {
6
9
  state: () => ({
@@ -59,25 +62,33 @@ export const useInfraStore = defineStore("infra", {
59
62
  }
60
63
  console.log("[INFRA] Lock granted for create_backend")
61
64
 
62
- if (this.app_mode === appMode.DESKTOP) {
63
- console.log("[INFRA] DESKTOP mode - Launching microservices...")
65
+ if (this.app_mode === appMode.CLOUD) {
66
+ console.log("[INFRA] CLOUD mode - Launching lambda...")
67
+ const lambdaStore = useLambdaStore()
68
+ this.ID = await lambdaStore.launch()
69
+ console.log("[INFRA] Lambda launched successfully")
70
+ } else {
71
+ console.log(
72
+ `[INFRA] ${this.app_mode} mode - Launching microservices...`,
73
+ )
74
+
75
+ const appStore = useAppStore()
76
+ await appStore.createProjectFolder()
77
+
78
+ if (this.app_mode === appMode.DESKTOP) {
79
+ globalThis.electronAPI.project_folder_path({
80
+ projectFolderPath: appStore.projectFolderPath,
81
+ })
82
+ }
83
+
64
84
  const microservices_with_launch = this.microservices.filter(
65
85
  (store) => store.launch,
66
86
  )
67
87
 
68
- const port_promises = microservices_with_launch.map((store) =>
69
- store.launch(),
88
+ const launch_promises = microservices_with_launch.map((store) =>
89
+ store.launch({ projectFolderPath: appStore.projectFolderPath }),
70
90
  )
71
- const ports = await Promise.all(port_promises)
72
-
73
- for (const [index, store] of microservices_with_launch.entries()) {
74
- store.$patch({ default_local_port: ports[index] })
75
- }
76
- } else if (this.app_mode === appMode.CLOUD) {
77
- console.log("[INFRA] CLOUD mode - Launching lambda...")
78
- const lambdaStore = useLambdaStore()
79
- this.ID = await lambdaStore.launch()
80
- console.log("[INFRA] Lambda launched successfully")
91
+ await Promise.all(launch_promises, registerRunningExtensions())
81
92
  }
82
93
 
83
94
  this.status = Status.CREATED
@@ -15,7 +15,8 @@ export const useLambdaStore = defineStore("lambda", {
15
15
  base_url() {
16
16
  const public_runtime_config = useRuntimeConfig().public
17
17
  const domain_name = public_runtime_config.API_URL
18
- const url = `${this.protocol}://${domain_name}:${this.port}${public_runtime_config.SITE_BRANCH}${public_runtime_config.PROJECT}/createbackend`
18
+ const projectPath = `/${public_runtime_config.PROJECT}`
19
+ const url = `${this.protocol}://${domain_name}:${this.port}${public_runtime_config.SITE_BRANCH}${projectPath}/createbackend`
19
20
  return url
20
21
  },
21
22
  is_busy() {
@@ -1,3 +1,4 @@
1
+ // Third party imports
1
2
  import vtkWSLinkClient, {
2
3
  newInstance,
3
4
  } from "@kitware/vtk.js/IO/Core/WSLinkClient"
@@ -5,9 +6,13 @@ import vtkWSLinkClient, {
5
6
  import _ from "lodash"
6
7
  // oxlint-disable-next-line no-unassigned-import
7
8
  import "@kitware/vtk.js/Rendering/OpenGL/Profiles/Geometry"
9
+ import schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json"
10
+ import { useRuntimeConfig } from "nuxt/app"
11
+
12
+ // Local imports
8
13
  import { Status } from "@ogw_front/utils/status"
9
14
  import { appMode } from "@ogw_front/utils/app_mode"
10
- import schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json"
15
+ import { useAppStore } from "@ogw_front/stores/app"
11
16
  import { useInfraStore } from "@ogw_front/stores/infra"
12
17
  import { viewer_call } from "@ogw_internal/utils/viewer_call"
13
18
 
@@ -39,10 +44,6 @@ export const useViewerStore = defineStore(
39
44
  if (infraStore.app_mode === appMode.CLOUD) {
40
45
  return "443"
41
46
  }
42
- const { VIEWER_PORT } = useRuntimeConfig().public
43
- if (VIEWER_PORT !== undefined && VIEWER_PORT !== "") {
44
- return VIEWER_PORT
45
- }
46
47
  return default_local_port.value
47
48
  })
48
49
 
@@ -149,12 +150,40 @@ export const useViewerStore = defineStore(
149
150
  request_counter.value -= 1
150
151
  }
151
152
 
152
- async function launch() {
153
- status.value = Status.CREATING
154
- console.log("[VIEWER] Launching viewer microservice...")
155
- const launched_port = await globalThis.electronAPI.run_viewer()
156
- console.log("[VIEWER] Viewer launched on port:", launched_port)
157
- return launched_port
153
+ function launch(args = { projectFolderPath }) {
154
+ console.log("[VIEWER] Launching viewer microservice...", { args })
155
+ const appStore = useAppStore()
156
+
157
+ const { VIEWER_PATH, VIEWER_COMMAND } = useRuntimeConfig().public
158
+
159
+ console.log("[VIEWER] VIEWER_PATH", VIEWER_PATH)
160
+ console.log("[VIEWER] VIEWER_COMMAND", VIEWER_COMMAND)
161
+ const schema = {
162
+ $id: "/api/app/run_viewer",
163
+ methods: ["POST"],
164
+ type: "object",
165
+ properties: {
166
+ VIEWER_PATH: { type: "string" },
167
+ VIEWER_COMMAND: { type: "string" },
168
+ },
169
+ required: ["VIEWER_PATH", "VIEWER_COMMAND"],
170
+ additionalProperties: true,
171
+ }
172
+
173
+ const params = {
174
+ VIEWER_PATH,
175
+ VIEWER_COMMAND,
176
+ args,
177
+ }
178
+
179
+ console.log("[VIEWER] params", params)
180
+
181
+ return appStore.request(schema, params, {
182
+ response_function: (response) => {
183
+ console.log(`[VIEWER] Viewer launched on port ${response.port}`)
184
+ this.default_local_port = response.port
185
+ },
186
+ })
158
187
  }
159
188
 
160
189
  async function connect() {
@@ -0,0 +1,45 @@
1
+ // Node.js imports
2
+ import path from "node:path"
3
+
4
+ // Third party imports
5
+ import Conf from "conf"
6
+
7
+ // Local imports
8
+
9
+ function projectConf(projectName) {
10
+ const projectConfig = new Conf({ projectName })
11
+ console.log(projectConf.name, { projectConfig })
12
+ return projectConfig
13
+ }
14
+
15
+ function confFolderPath(projectName) {
16
+ const projectConfig = projectConf(projectName)
17
+ return path.dirname(projectConfig.path)
18
+ }
19
+
20
+ function extensionsConf(projectName) {
21
+ const projectConfig = projectConf(projectName)
22
+ if (!projectConfig.has("extensions")) {
23
+ projectConfig.set("extensions", {})
24
+ }
25
+ const extensionsConfig = projectConfig.get("extensions")
26
+ return extensionsConfig
27
+ }
28
+
29
+ function addExtensionToConf(projectName, { extensionID, extensionPath }) {
30
+ const projectConfig = projectConf(projectName)
31
+ projectConfig.set(`extensions.${extensionID}.path`, extensionPath)
32
+ }
33
+
34
+ function extensionPathFromConf(projectName, extensionID) {
35
+ const projectConfig = projectConf(projectName)
36
+ return projectConfig.get(`extensions.${extensionID}.path`)
37
+ }
38
+
39
+ export {
40
+ confFolderPath,
41
+ projectConf,
42
+ extensionsConf,
43
+ addExtensionToConf,
44
+ extensionPathFromConf,
45
+ }
@@ -0,0 +1,109 @@
1
+ // Node.js imports
2
+
3
+ // Third party imports
4
+ import _ from "lodash"
5
+ import { useRuntimeConfig } from "nuxt/app"
6
+
7
+ // Local imports
8
+ import { useAppStore } from "@ogw_front/stores/app"
9
+ import { useInfraStore } from "@ogw_front/stores/infra"
10
+
11
+ async function importExtensionFile(file) {
12
+ await uploadExtension(file)
13
+ return registerRunningExtensions()
14
+ }
15
+
16
+ async function registerRunningExtensions() {
17
+ const appStore = useAppStore()
18
+ const infraStore = useInfraStore()
19
+ const { extensionsArray } = await runExtensions()
20
+
21
+ for (const extension of extensionsArray) {
22
+ const { id, name, version, frontendContent, port } = extension
23
+ const blob = new Blob([frontendContent], {
24
+ type: "application/javascript",
25
+ })
26
+ const blobUrl = URL.createObjectURL(blob)
27
+ const extensionModule = await appStore.loadExtension(blobUrl)
28
+ console.log("[ExtensionManager] Extension loaded:", id)
29
+
30
+ if (extensionModule.metadata?.store) {
31
+ const storeFactory = extensionModule.metadata.store
32
+ const store = storeFactory()
33
+ store.$patch((state) => {
34
+ state.default_local_port = port
35
+ })
36
+ appStore.registerStore(store)
37
+ console.log("[ExtensionManager] Store registered:", store.$id)
38
+ await store.connect()
39
+ console.log("[ExtensionManager] Microservice connected:", store.$id)
40
+ infraStore.register_microservice(store)
41
+ }
42
+ return {
43
+ name,
44
+ version,
45
+ extensionModule,
46
+ }
47
+ }
48
+ }
49
+
50
+ async function unloadExtension(extensionId) {
51
+ const appStore = useAppStore()
52
+ console.log("[ExtensionManager] Unloading extension:", extensionId)
53
+
54
+ const extensionData = appStore.getExtension(extensionId)
55
+ if (!extensionData) {
56
+ console.warn("[ExtensionManager] Extension not found:", extensionId)
57
+ return false
58
+ }
59
+
60
+ // Get the store if it exists
61
+ const storeFactory = extensionData.metadata?.store
62
+ if (storeFactory) {
63
+ const store = storeFactory()
64
+ // Stop the microservice if possible
65
+ if (typeof store.kill === "function") {
66
+ await store.kill()
67
+ }
68
+ }
69
+
70
+ // Unload from AppStore
71
+ appStore.unloadExtension(extensionId)
72
+
73
+ console.log("[ExtensionManager] Extension unloaded:", extensionId)
74
+ return true
75
+ }
76
+
77
+ async function uploadExtension(file) {
78
+ const appStore = useAppStore()
79
+ await appStore.upload(file)
80
+ }
81
+
82
+ async function runExtensions() {
83
+ const appStore = useAppStore()
84
+ const projectFolderPath = appStore.projectFolderPath
85
+ const { PROJECT: projectName } = useRuntimeConfig().public
86
+ const params = { projectFolderPath, projectName }
87
+
88
+ const schema = {
89
+ $id: "/api/extensions/run",
90
+ methods: ["POST"],
91
+ type: "object",
92
+ properties: {
93
+ projectFolderPath: { type: "string" },
94
+ projectName: { type: "string" },
95
+ },
96
+ required: ["projectFolderPath", "projectName"],
97
+ additionalProperties: false,
98
+ }
99
+
100
+ return appStore.request(schema, params)
101
+ }
102
+
103
+ export {
104
+ importExtensionFile,
105
+ unloadExtension,
106
+ uploadExtension,
107
+ registerRunningExtensions,
108
+ runExtensions,
109
+ }