@geode/opengeodeweb-front 10.6.0 → 10.6.1-rc.2

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 (32) hide show
  1. package/.oxlintrc.json +3 -1
  2. package/app/stores/app.js +1 -1
  3. package/app/stores/geode.js +3 -3
  4. package/app/stores/infra.js +3 -10
  5. package/app/stores/viewer.js +17 -30
  6. package/app/utils/extension.js +30 -28
  7. package/app/utils/file_import_workflow.js +1 -0
  8. package/app/utils/local/microservices.js +10 -8
  9. package/app/utils/local/scripts.js +1 -1
  10. package/app/utils/server.js +5 -4
  11. package/internal/utils/api_fetch.js +0 -1
  12. package/microservices.json +3 -0
  13. package/package.json +4 -4
  14. package/server/api/app/kill.post.js +1 -0
  15. package/server/api/extensions/run.post.js +59 -58
  16. package/server/api/extensions/upload.put.js +63 -61
  17. package/tests/integration/setup.js +6 -6
  18. package/tests/integration/stores/data_style/mesh/cells.nuxt.test.js +2 -2
  19. package/tests/integration/stores/data_style/mesh/edges.nuxt.test.js +2 -2
  20. package/tests/integration/stores/data_style/mesh/index.nuxt.test.js +2 -2
  21. package/tests/integration/stores/data_style/mesh/points.nuxt.test.js +2 -2
  22. package/tests/integration/stores/data_style/mesh/polygons.nuxt.test.js +2 -2
  23. package/tests/integration/stores/data_style/mesh/polyhedra.nuxt.test.js +2 -2
  24. package/tests/integration/stores/data_style/model/blocks.nuxt.test.js +2 -2
  25. package/tests/integration/stores/data_style/model/corners.nuxt.test.js +2 -2
  26. package/tests/integration/stores/data_style/model/edges.nuxt.test.js +2 -2
  27. package/tests/integration/stores/data_style/model/index.nuxt.test.js +2 -2
  28. package/tests/integration/stores/data_style/model/lines.nuxt.test.js +2 -2
  29. package/tests/integration/stores/data_style/model/points.nuxt.test.js +2 -2
  30. package/tests/integration/stores/data_style/model/surfaces.nuxt.test.js +2 -2
  31. package/tests/integration/stores/viewer.nuxt.test.js +2 -2
  32. package/tests/unit/stores/infra.nuxt.test.js +1 -6
package/.oxlintrc.json CHANGED
@@ -38,7 +38,8 @@
38
38
  "id",
39
39
  "ID",
40
40
  "fs",
41
- "os"
41
+ "os",
42
+ "_"
42
43
  ],
43
44
  "min": 3
44
45
  }
@@ -53,6 +54,7 @@
53
54
  "oxc/no-optional-chaining": "off",
54
55
  "node/no-process-env": "off",
55
56
  "no-continue": "off",
57
+ "vitest/require-hook": "off",
56
58
  "import/unambiguous": "off",
57
59
  "max-params": ["warn", { "max": 4 }],
58
60
  "import/no-nodejs-modules": "off",
package/app/stores/app.js CHANGED
@@ -1,5 +1,5 @@
1
- import { upload_file } from "@ogw_internal/utils/upload_file.js"
2
1
  import { api_fetch } from "@ogw_internal/utils/api_fetch.js"
2
+ import { upload_file } from "@ogw_internal/utils/upload_file.js"
3
3
 
4
4
  export const useAppStore = defineStore("app", () => {
5
5
  const stores = []
@@ -2,10 +2,10 @@ import { Status } from "@ogw_front/utils/status"
2
2
  import { api_fetch } from "@ogw_internal/utils/api_fetch"
3
3
  import { appMode } from "@ogw_front/utils/app_mode"
4
4
  import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json"
5
+ import { upload_file } from "@ogw_internal/utils/upload_file.js"
6
+ import { useAppStore } from "@ogw_front/stores/app"
5
7
  import { useFeedbackStore } from "@ogw_front/stores/feedback"
6
8
  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"
9
9
 
10
10
  const MILLISECONDS_IN_SECOND = 1000
11
11
  const DEFAULT_PING_INTERVAL_SECONDS = 10
@@ -132,7 +132,7 @@ export const useGeodeStore = defineStore("geode", {
132
132
  "[GEODE] Request completed:",
133
133
  schema.$id,
134
134
  "in",
135
- (Date.now() - start) / 1_000,
135
+ (Date.now() - start) / MILLISECONDS_IN_SECOND,
136
136
  "s",
137
137
  )
138
138
  if (callbacks.response_function) {
@@ -1,7 +1,7 @@
1
1
  import { appMode, getAppMode } from "@ogw_front/utils/app_mode"
2
2
  import { Status } from "@ogw_front/utils/status"
3
- import { useLambdaStore } from "@ogw_front/stores/lambda"
4
3
  import { useAppStore } from "@ogw_front/stores/app"
4
+ import { useLambdaStore } from "@ogw_front/stores/lambda"
5
5
 
6
6
  import { registerRunningExtensions } from "@ogw_front/utils/extension"
7
7
 
@@ -50,18 +50,15 @@ export const useInfraStore = defineStore("infra", {
50
50
  "[INFRA] Registered microservices:",
51
51
  this.microservices.map((store) => store.$id),
52
52
  )
53
-
54
53
  if (this.status === Status.CREATED) {
55
54
  return
56
55
  }
57
-
58
56
  return navigator.locks.request("infra.create_backend", async () => {
59
57
  this.status = Status.CREATING
60
58
  if (this.status === Status.CREATED) {
61
59
  return
62
60
  }
63
61
  console.log("[INFRA] Lock granted for create_backend")
64
-
65
62
  if (this.app_mode === appMode.CLOUD) {
66
63
  console.log("[INFRA] CLOUD mode - Launching lambda...")
67
64
  const lambdaStore = useLambdaStore()
@@ -71,26 +68,22 @@ export const useInfraStore = defineStore("infra", {
71
68
  console.log(
72
69
  `[INFRA] ${this.app_mode} mode - Launching microservices...`,
73
70
  )
74
-
75
71
  const appStore = useAppStore()
76
72
  await appStore.createProjectFolder()
77
-
78
73
  if (this.app_mode === appMode.DESKTOP) {
79
74
  globalThis.electronAPI.project_folder_path({
80
75
  projectFolderPath: appStore.projectFolderPath,
81
76
  })
82
77
  }
83
-
84
78
  const microservices_with_launch = this.microservices.filter(
85
79
  (store) => store.launch,
86
80
  )
87
-
88
81
  const launch_promises = microservices_with_launch.map((store) =>
89
82
  store.launch({ projectFolderPath: appStore.projectFolderPath }),
90
83
  )
91
- await Promise.all(launch_promises, registerRunningExtensions())
84
+ launch_promises.push(registerRunningExtensions())
85
+ await Promise.all(launch_promises)
92
86
  }
93
-
94
87
  this.status = Status.CREATED
95
88
  console.log("[INFRA] Backend created successfully")
96
89
  return this.create_connection()
@@ -2,10 +2,11 @@
2
2
  import vtkWSLinkClient, {
3
3
  newInstance,
4
4
  } from "@kitware/vtk.js/IO/Core/WSLinkClient"
5
- // oxlint-disable-next-line id-length
6
5
  import _ from "lodash"
7
6
  // oxlint-disable-next-line no-unassigned-import
8
7
  import "@kitware/vtk.js/Rendering/OpenGL/Profiles/Geometry"
8
+ import SmartConnect from "wslink/src/SmartConnect"
9
+ import { connectImageStream } from "@kitware/vtk.js/Rendering/Misc/RemoteView"
9
10
  import schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json"
10
11
  import { useRuntimeConfig } from "nuxt/app"
11
12
 
@@ -22,6 +23,7 @@ const request_timeout = MS_PER_SECOND * SECONDS_PER_REQUEST
22
23
 
23
24
  export const useViewerStore = defineStore(
24
25
  "viewer",
26
+ // oxlint-disable-next-line max-lines-per-function max-statements
25
27
  () => {
26
28
  const infraStore = useInfraStore()
27
29
  const default_local_port = ref("1234")
@@ -76,7 +78,7 @@ export const useViewerStore = defineStore(
76
78
  picking_mode.value = false
77
79
  }
78
80
 
79
- async function ws_connect() {
81
+ function ws_connect() {
80
82
  if (status.value === Status.CONNECTED) {
81
83
  return
82
84
  }
@@ -87,46 +89,31 @@ export const useViewerStore = defineStore(
87
89
  try {
88
90
  console.log("VIEWER LOCK GRANTED !", lock)
89
91
  status.value = Status.CONNECTING
90
- const { default: SmartConnect } =
91
- await import("wslink/src/SmartConnect")
92
92
  vtkWSLinkClient.setSmartConnectClass(SmartConnect)
93
93
 
94
- const config_obj = { application: "Viewer" }
95
- config_obj.sessionURL = base_url.value
96
-
97
- if (status.value === Status.CONNECTED && client.value.isConnected()) {
98
- client.value.disconnect(-1)
99
- status.value = Status.NOT_CONNECTED
100
- }
101
- let clientToConnect = client.value
102
- if (_.isEmpty(clientToConnect)) {
103
- clientToConnect = newInstance()
94
+ if (_.isEmpty(client.value)) {
95
+ client.value = newInstance()
104
96
  }
105
97
 
106
- // Connect to busy store
107
- clientToConnect.onBusyChange((count) => {
98
+ client.value.onBusyChange((count) => {
108
99
  buzy.value = count
109
100
  })
110
- clientToConnect.beginBusy()
111
-
112
- // Error
113
- clientToConnect.onConnectionError((httpReq) => {
101
+ client.value.onConnectionError((httpReq) => {
114
102
  const message = httpReq?.response?.error || `Connection error`
115
103
  console.error(message)
116
104
  })
117
-
118
- // Close
119
- clientToConnect.onConnectionClose((httpReq) => {
105
+ client.value.onConnectionClose((httpReq) => {
120
106
  const message = httpReq?.response?.error || `Connection close`
121
107
  console.error(message)
122
108
  })
123
109
 
124
- // Connect
125
- const { connectImageStream } =
126
- await import("@kitware/vtk.js/Rendering/Misc/RemoteView")
127
- client.value = await clientToConnect.connect(config_obj)
110
+ client.value.beginBusy()
111
+ await client.value.connect({
112
+ application: "Viewer",
113
+ sessionURL: base_url.value,
114
+ })
128
115
  connectImageStream(client.value.getConnection().getSession())
129
- clientToConnect.endBusy()
116
+ client.value.endBusy()
130
117
  await request(
131
118
  schemas.opengeodeweb_viewer.viewer.reset_visualization,
132
119
  {},
@@ -135,7 +122,7 @@ export const useViewerStore = defineStore(
135
122
  )
136
123
  status.value = Status.CONNECTED
137
124
  } catch (error) {
138
- console.error("error", error)
125
+ console.error("ws_connect error", error)
139
126
  status.value = Status.NOT_CONNECTED
140
127
  throw error
141
128
  }
@@ -214,7 +201,7 @@ export const useViewerStore = defineStore(
214
201
  "[VIEWER] Request completed:",
215
202
  schema.$id,
216
203
  "in",
217
- (Date.now() - start) / 1_000,
204
+ (Date.now() - start) / MS_PER_SECOND,
218
205
  "s",
219
206
  )
220
207
  if (callbacks.response_function) {
@@ -18,33 +18,35 @@ async function registerRunningExtensions() {
18
18
  const infraStore = useInfraStore()
19
19
  const { extensionsArray } = await runExtensions()
20
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
21
+ return Promise.all(
22
+ extensionsArray.map(async (extension) => {
23
+ const { name, version, frontendContent, port } = extension
24
+ const blob = new Blob([frontendContent], {
25
+ type: "application/javascript",
35
26
  })
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
- }
27
+ const blobUrl = URL.createObjectURL(blob)
28
+ const extensionModule = await appStore.loadExtension(blobUrl)
29
+ console.log("[ExtensionManager] Extension loaded:", id)
30
+
31
+ if (extensionModule.metadata?.store) {
32
+ const storeFactory = extensionModule.metadata.store
33
+ const store = storeFactory()
34
+ store.$patch((state) => {
35
+ state.default_local_port = port
36
+ })
37
+ appStore.registerStore(store)
38
+ console.log("[ExtensionManager] Store registered:", store.$id)
39
+ await store.connect()
40
+ console.log("[ExtensionManager] Microservice connected:", store.$id)
41
+ infraStore.register_microservice(store)
42
+ }
43
+ return {
44
+ name,
45
+ version,
46
+ extensionModule,
47
+ }
48
+ }),
49
+ )
48
50
  }
49
51
 
50
52
  async function unloadExtension(extensionId) {
@@ -79,9 +81,9 @@ async function uploadExtension(file) {
79
81
  await appStore.upload(file)
80
82
  }
81
83
 
82
- async function runExtensions() {
84
+ function runExtensions() {
83
85
  const appStore = useAppStore()
84
- const projectFolderPath = appStore.projectFolderPath
86
+ const { projectFolderPath } = appStore
85
87
  const { PROJECT: projectName } = useRuntimeConfig().public
86
88
  const params = { projectFolderPath, projectName }
87
89
 
@@ -1,3 +1,4 @@
1
+ // oxlint-disable promise/prefer-await-to-then
1
2
  // Third party imports
2
3
  import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json"
3
4
 
@@ -5,17 +5,17 @@ import path from "node:path"
5
5
 
6
6
  // Third party imports
7
7
  import { WebSocket } from "ws"
8
+ import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json" with { type: "json" }
8
9
  import { getPort } from "get-port-please"
9
10
  import pTimeout from "p-timeout"
10
- import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json" with { type: "json" }
11
11
 
12
12
  // Local imports
13
+ import { commandExistsSync, waitForReady } from "./scripts.js"
13
14
  import {
14
15
  deleteFolderRecursive,
15
- executablePath,
16
16
  executableName,
17
+ executablePath,
17
18
  } from "./path.js"
18
- import { commandExistsSync, waitForReady } from "./scripts.js"
19
19
 
20
20
  const DEFAULT_TIMEOUT_SECONDS = 30
21
21
  const MILLISECONDS_PER_SECOND = 1000
@@ -143,6 +143,7 @@ function killWebsocketMicroservice(microservice) {
143
143
  const failMessage = `Failed to kill ${microservice.name}`
144
144
  const successMessage = `Disconnected from ${microservice.name} WebSocket server`
145
145
  function do_kill() {
146
+ // oxlint-disable-next-line promise/avoid-new
146
147
  return new Promise((resolve) => {
147
148
  const socket = new WebSocket(microservice.url)
148
149
  socket.on("open", () => {
@@ -213,11 +214,11 @@ function projectMicroservices(projectFolderPath) {
213
214
  const microservicesMetadatas = { microservices: [] }
214
215
  fs.writeFileSync(
215
216
  filePath,
216
- JSON.stringify(microservicesMetadatas, null, 2),
217
- "utf-8",
217
+ JSON.stringify(microservicesMetadatas, undefined, 2),
218
+ "utf8",
218
219
  )
219
220
  }
220
- const content = JSON.parse(fs.readFileSync(filePath, "utf-8"))
221
+ const content = JSON.parse(fs.readFileSync(filePath, "utf8"))
221
222
  return content.microservices
222
223
  }
223
224
 
@@ -236,7 +237,8 @@ function addMicroserviceMetadatas(projectFolderPath, serviceObj) {
236
237
  if (serviceObj.type === "back") {
237
238
  const schema = back_schemas.opengeodeweb_back.kill
238
239
  serviceObj.url = `http://localhost:${serviceObj.port}/${schema.$id}`
239
- serviceObj.method = schema.methods[0]
240
+ const [method] = schema.methods
241
+ serviceObj.method = method
240
242
  } else if (serviceObj.type === "viewer") {
241
243
  serviceObj.url = `ws://localhost:${serviceObj.port}/ws`
242
244
  }
@@ -244,7 +246,7 @@ function addMicroserviceMetadatas(projectFolderPath, serviceObj) {
244
246
  microservices.push(serviceObj)
245
247
  fs.writeFileSync(
246
248
  microservicesMetadatasPath(projectFolderPath),
247
- JSON.stringify({ microservices }, null, 2),
249
+ JSON.stringify({ microservices }, undefined, 2),
248
250
  )
249
251
  }
250
252
 
@@ -1,7 +1,7 @@
1
1
  // Node imports
2
- import { on } from "node:events"
3
2
  import child_process from "node:child_process"
4
3
  import fs from "node:fs"
4
+ import { on } from "node:events"
5
5
  import path from "node:path"
6
6
 
7
7
  function commandExistsSync(execName) {
@@ -16,22 +16,23 @@ async function unzipFile(
16
16
  await fs.promises.mkdir(outputDir, { recursive: true })
17
17
  const promises = []
18
18
 
19
- zip.forEach((relativePath, zipEntry) => {
19
+ for (const [relativePath, zipEntry] of Object.entries(zip.files)) {
20
20
  const outputPath = path.join(outputDir, relativePath)
21
21
 
22
22
  if (zipEntry.dir) {
23
23
  promises.push(fs.promises.mkdir(outputPath, { recursive: true }))
24
24
  } else {
25
25
  promises.push(
26
- zipEntry.async("nodebuffer").then(async (content) => {
26
+ (async () => {
27
+ const content = await zipEntry.async("nodebuffer")
27
28
  await fs.promises.mkdir(path.dirname(outputPath), {
28
29
  recursive: true,
29
30
  })
30
31
  await fs.promises.writeFile(outputPath, content)
31
- }),
32
+ })(),
32
33
  )
33
34
  }
34
- })
35
+ }
35
36
 
36
37
  await Promise.all(promises)
37
38
  console.log("Extraction complete!")
@@ -1,4 +1,3 @@
1
- // oxlint-disable-next-line id-length
2
1
  import _ from "lodash"
3
2
  import { useFeedbackStore } from "@ogw_front/stores/feedback"
4
3
  import { validate_schema } from "@ogw_front/utils/validate_schema"
@@ -0,0 +1,3 @@
1
+ {
2
+ "microservices": []
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geode/opengeodeweb-front",
3
- "version": "10.6.0",
3
+ "version": "10.6.1-rc.2",
4
4
  "description": "OpenSource Vue/Nuxt/Pinia/Vuetify framework for web applications",
5
5
  "homepage": "https://github.com/Geode-solutions/OpenGeodeWeb-Front",
6
6
  "bugs": {
@@ -31,15 +31,15 @@
31
31
  "build": ""
32
32
  },
33
33
  "dependencies": {
34
- "@geode/opengeodeweb-back": "latest",
35
- "@geode/opengeodeweb-viewer": "latest",
34
+ "@geode/opengeodeweb-back": "next",
35
+ "@geode/opengeodeweb-viewer": "next",
36
36
  "@kitware/vtk.js": "33.3.0",
37
37
  "@mdi/font": "7.4.47",
38
38
  "@pinia/nuxt": "0.11.3",
39
39
  "@types/node": "22.15.3",
40
40
  "@vueuse/components": "13.1.0",
41
41
  "@vueuse/nuxt": "13.1.0",
42
- "@vueuse/rxjs": "^14.1.0",
42
+ "@vueuse/rxjs": "14.1.0",
43
43
  "ajv": "8.17.1",
44
44
  "busboy": "1.6.0",
45
45
  "conf": "15.1.0",
@@ -7,5 +7,6 @@ import { defineEventHandler } from "h3"
7
7
 
8
8
  export default defineEventHandler(() => {
9
9
  console.log("Killing node server process")
10
+ // oxlint-disable-next-line no-process-exit
10
11
  process.exit()
11
12
  })
@@ -6,12 +6,12 @@ import path from "node:path"
6
6
  import { createError, defineEventHandler, readBody } from "h3"
7
7
 
8
8
  // Local imports
9
- import { extensionsConf } from "@geode/opengeodeweb-front/app/utils/config.js"
10
- import { unzipFile } from "@geode/opengeodeweb-front/app/utils/server.js"
11
9
  import {
12
10
  addMicroserviceMetadatas,
13
11
  runBack,
14
12
  } from "@geode/opengeodeweb-front/app/utils/local/microservices.js"
13
+ import { extensionsConf } from "@geode/opengeodeweb-front/app/utils/config.js"
14
+ import { unzipFile } from "@geode/opengeodeweb-front/app/utils/server.js"
15
15
 
16
16
  const CODE_200 = 200
17
17
 
@@ -21,68 +21,69 @@ export default defineEventHandler(async (event) => {
21
21
  const { projectFolderPath, projectName } = body
22
22
  const extensionsConfig = extensionsConf(projectName)
23
23
 
24
- const extensionsArray = []
24
+ const extensionsArray = await Promise.all(
25
+ Object.keys(extensionsConfig).map(async (extensionID) => {
26
+ const extensionPath = extensionsConfig[extensionID].path
27
+ const unzippedExtensionPath = await unzipFile(
28
+ extensionPath,
29
+ path.join(projectFolderPath, "extensions"),
30
+ )
31
+ const metadataPath = path.join(unzippedExtensionPath, "metadata.json")
32
+ const metadataContent = await fs.promises.readFile(metadataPath, "utf8")
25
33
 
26
- for (const extensionID of Object.keys(extensionsConfig)) {
27
- const extensionPath = extensionsConfig[extensionID].path
28
- const unzippedExtensionPath = await unzipFile(
29
- extensionPath,
30
- path.join(projectFolderPath, "extensions"),
31
- )
32
- const metadataPath = path.join(unzippedExtensionPath, "metadata.json")
33
- const metadataContent = await fs.promises.readFile(metadataPath, "utf-8")
34
+ if (!metadataContent) {
35
+ throw createError({
36
+ statusCode: 400,
37
+ statusMessage: "Invalid extension file: missing metadata.json",
38
+ })
39
+ }
40
+ const metadata = JSON.parse(metadataContent)
41
+ console.log("runExtensions", { metadata })
34
42
 
35
- if (!metadataContent) {
36
- throw createError({
37
- statusCode: 400,
38
- statusMessage: "Invalid extension file: missing metadata.json",
39
- })
40
- }
41
- const metadata = JSON.parse(metadataContent)
42
- console.log("runExtensions", { metadata })
43
+ const { id, name, version, backendExecutable, frontendFile } = metadata
44
+ console.log("runExtensions", { id, name, version, backendExecutable })
43
45
 
44
- const { id, name, version, backendExecutable, frontendFile } = metadata
45
- console.log("runExtensions", { id, name, version, backendExecutable })
46
+ if (!frontendFile) {
47
+ throw createError({
48
+ statusCode: 400,
49
+ statusMessage:
50
+ "Invalid extension file: missing frontend JavaScript",
51
+ })
52
+ }
53
+ if (!backendExecutable) {
54
+ throw createError({
55
+ statusCode: 400,
56
+ statusMessage: "Invalid extension file: missing backend executable",
57
+ })
58
+ }
46
59
 
47
- if (!frontendFile) {
48
- throw createError({
49
- statusCode: 400,
50
- statusMessage: "Invalid extension file: missing frontend JavaScript",
60
+ const frontendFilePath = path.join(unzippedExtensionPath, frontendFile)
61
+ const frontendContent = await fs.promises.readFile(
62
+ frontendFilePath,
63
+ "utf8",
64
+ )
65
+ const backendExecutablePath = path.join(
66
+ unzippedExtensionPath,
67
+ backendExecutable,
68
+ )
69
+ fs.chmodSync(backendExecutablePath, "755")
70
+ const port = await runBack(backendExecutable, backendExecutablePath, {
71
+ projectFolderPath,
51
72
  })
52
- }
53
- if (!backendExecutable) {
54
- throw createError({
55
- statusCode: 400,
56
- statusMessage: "Invalid extension file: missing backend executable",
73
+ await addMicroserviceMetadatas(projectFolderPath, {
74
+ type: "back",
75
+ name,
76
+ port,
57
77
  })
58
- }
59
-
60
- const frontendFilePath = path.join(unzippedExtensionPath, frontendFile)
61
- const frontendContent = await fs.promises.readFile(
62
- frontendFilePath,
63
- "utf-8",
64
- )
65
- const backendExecutablePath = path.join(
66
- unzippedExtensionPath,
67
- backendExecutable,
68
- )
69
- await fs.chmodSync(backendExecutablePath, "755")
70
- const port = await runBack(backendExecutable, backendExecutablePath, {
71
- projectFolderPath,
72
- })
73
- extensionsArray.push({
74
- id,
75
- name,
76
- version,
77
- frontendContent,
78
- port,
79
- })
80
- await addMicroserviceMetadatas(projectFolderPath, {
81
- type: "back",
82
- name,
83
- port,
84
- })
85
- }
78
+ return {
79
+ id,
80
+ name,
81
+ version,
82
+ frontendContent,
83
+ port,
84
+ }
85
+ }),
86
+ )
86
87
 
87
88
  return {
88
89
  statusCode: CODE_200,
@@ -1,27 +1,28 @@
1
1
  // Node imports
2
+ import { finished, pipeline } from "node:stream/promises"
3
+ import { Readable } from "node:stream"
2
4
  import fs from "node:fs"
3
5
  import path from "node:path"
4
- import { Readable } from "node:stream"
5
6
 
6
7
  // Third party imports
7
- import busboy from "busboy"
8
8
  import {
9
9
  createError,
10
10
  defineEventHandler,
11
11
  getRequestHeaders,
12
12
  getRequestWebStream,
13
13
  } from "h3"
14
-
15
- import sanitize from "sanitize-filename"
16
14
  import StreamZip from "node-stream-zip"
15
+ import busboy from "busboy"
16
+ import sanitize from "sanitize-filename"
17
17
 
18
18
  // Local imports
19
19
  import {
20
- confFolderPath,
21
20
  addExtensionToConf,
21
+ confFolderPath,
22
22
  } from "@geode/opengeodeweb-front/app/utils/config.js"
23
23
 
24
24
  const CODE_201 = 201
25
+ const FILE_SIZE_LIMIT = 107_374_182 // 100 MB
25
26
 
26
27
  export default defineEventHandler(async (event) => {
27
28
  const projectName = "vease"
@@ -30,53 +31,51 @@ export default defineEventHandler(async (event) => {
30
31
 
31
32
  const configFolderPath = confFolderPath(projectName)
32
33
 
33
- await new Promise((resolve, reject) => {
34
- const bb = busboy({
35
- headers: getRequestHeaders(event),
36
- limits: {
37
- fileSize: 1024 * 1024 * 1024 * 0.1, // 100 MB
38
- files: 1,
39
- },
40
- })
41
-
42
- bb.on("file", (fieldname, fileStream, info) => {
43
- if (fieldname !== "file") {
44
- fileStream.resume() // drain & ignore unwanted fields
45
- return
46
- }
47
-
48
- const safeFilename = sanitize(info.filename)
49
- const targetPath = path.join(configFolderPath, safeFilename)
50
-
51
- const writePromise = new Promise((res, rej) => {
52
- const writeStream = fs.createWriteStream(targetPath)
53
-
54
- fileStream
55
- .pipe(writeStream)
56
- .on("finish", () => {
57
- savedFiles.push(targetPath)
58
- console.log("File written:", targetPath)
59
- res()
60
- })
61
- .on("error", rej)
62
- })
34
+ const busboyInstance = busboy({
35
+ headers: getRequestHeaders(event),
36
+ limits: {
37
+ fileSize: FILE_SIZE_LIMIT,
38
+ files: 1,
39
+ },
40
+ })
63
41
 
64
- writePromises.push(writePromise)
65
- fileStream.on("limit", () => reject(new Error("File too large")))
66
- })
42
+ busboyInstance.on("file", (fieldname, fileStream, info) => {
43
+ if (fieldname !== "file") {
44
+ // Drain & ignore unwanted fields
45
+ fileStream.resume()
46
+ return
47
+ }
48
+
49
+ const safeFilename = sanitize(info.filename)
50
+ const targetPath = path.join(configFolderPath, safeFilename)
51
+
52
+ const writePromise = (async () => {
53
+ const writeStream = fs.createWriteStream(targetPath)
54
+ await pipeline(fileStream, writeStream)
55
+ savedFiles.push(targetPath)
56
+ console.log("File written:", targetPath)
57
+ })()
58
+
59
+ writePromises.push(writePromise)
60
+ fileStream.on("limit", () =>
61
+ busboyInstance.destroy(new Error("File too large")),
62
+ )
63
+ })
67
64
 
68
- bb.on("field", (name, value) => {
69
- console.log(`Field ${name}: ${value}`)
70
- })
65
+ busboyInstance.on("field", (name, value) => {
66
+ console.log(`Field ${name}: ${value}`)
67
+ })
71
68
 
72
- bb.on("close", resolve)
73
- bb.on("error", reject)
74
- bb.on("filesLimit", () => reject(new Error("Too many files")))
75
- bb.on("partsLimit", () => reject(new Error("Too many parts")))
69
+ busboyInstance.on("filesLimit", () =>
70
+ busboyInstance.destroy(new Error("Too many files")),
71
+ )
72
+ busboyInstance.on("partsLimit", () =>
73
+ busboyInstance.destroy(new Error("Too many parts")),
74
+ )
76
75
 
77
- const webStream = getRequestWebStream(event)
78
- Readable.fromWeb(webStream).pipe(bb)
79
- })
76
+ const webStream = getRequestWebStream(event)
77
+ Readable.fromWeb(webStream).pipe(busboyInstance)
78
+ await finished(busboyInstance)
80
79
 
81
80
  if (writePromises.length > 0) {
82
81
  await Promise.all(writePromises)
@@ -87,19 +86,22 @@ export default defineEventHandler(async (event) => {
87
86
  throw createError({ statusCode: 400, message: "No file received" })
88
87
  }
89
88
 
90
- for (const file of savedFiles) {
91
- const zip = new StreamZip.async({
92
- file,
93
- storeEntries: true,
94
- })
95
- const metadataJson = await zip.entryData("metadata.json")
96
- const metadata = JSON.parse(metadataJson)
97
- const { id } = metadata
98
- await addExtensionToConf(projectName, {
99
- extensionID: id,
100
- extensionPath: file,
101
- })
102
- }
89
+ await Promise.all(
90
+ savedFiles.map(async (file) => {
91
+ const StreamZipAsync = StreamZip.async
92
+ const zip = new StreamZipAsync({
93
+ file,
94
+ storeEntries: true,
95
+ })
96
+ const metadataJson = await zip.entryData("metadata.json")
97
+ const metadata = JSON.parse(metadataJson)
98
+ const { id } = metadata
99
+ await addExtensionToConf(projectName, {
100
+ extensionID: id,
101
+ extensionPath: file,
102
+ })
103
+ }),
104
+ )
103
105
 
104
106
  return { statusCode: CODE_201 }
105
107
  })
@@ -1,28 +1,28 @@
1
1
  // Node.js imports
2
- import path from "node:path"
3
2
  import { WebSocket } from "ws"
3
+ import path from "node:path"
4
4
 
5
5
  // Third party imports
6
6
  import { afterAll, beforeAll, expect, vi } from "vitest"
7
7
  import { useRuntimeConfig } from "nuxt/app"
8
8
 
9
9
  // Local imports
10
- import {
11
- createPath,
12
- generateProjectFolderPath,
13
- } from "@ogw_front/utils/local/path"
14
10
  import {
15
11
  addMicroserviceMetadatas,
16
12
  runBack,
17
13
  runViewer,
18
14
  } from "@ogw_front/utils/local/microservices"
15
+ import {
16
+ createPath,
17
+ generateProjectFolderPath,
18
+ } from "@ogw_front/utils/local/path"
19
19
  import { Status } from "@ogw_front/utils/status"
20
20
  import { appMode } from "@ogw_front/utils/app_mode"
21
21
  import { importFile } from "@ogw_front/utils/file_import_workflow"
22
+ import { setupActivePinia } from "@ogw_tests/utils"
22
23
  import { useGeodeStore } from "@ogw_front/stores/geode"
23
24
  import { useInfraStore } from "@ogw_front/stores/infra"
24
25
  import { useViewerStore } from "@ogw_front/stores/viewer"
25
- import { setupActivePinia } from "@ogw_tests/utils"
26
26
 
27
27
  // Local constants
28
28
  const data_folder = path.join("tests", "integration", "data", "uploads")
@@ -3,11 +3,11 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
3
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" with { type: "json" }
4
4
 
5
5
  // Local imports
6
- import { cleanupBackend } from "@ogw_front/utils/local/microservices"
7
6
  import { Status } from "@ogw_front/utils/status"
7
+ import { cleanupBackend } from "@ogw_front/utils/local/microservices"
8
+ import { setupIntegrationTests } from "@ogw_tests/integration/setup"
8
9
  import { useDataStyleStore } from "@ogw_front/stores/data_style"
9
10
  import { useViewerStore } from "@ogw_front/stores/viewer"
10
- import { setupIntegrationTests } from "@ogw_tests/integration/setup"
11
11
 
12
12
  // Local constants
13
13
  const INTERVAL_TIMEOUT = 20_000
@@ -3,11 +3,11 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
3
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" with { type: "json" }
4
4
 
5
5
  // Local imports
6
- import { cleanupBackend } from "@ogw_front/utils/local/microservices"
7
6
  import { Status } from "@ogw_front/utils/status"
7
+ import { cleanupBackend } from "@ogw_front/utils/local/microservices"
8
+ import { setupIntegrationTests } from "@ogw_tests/integration/setup"
8
9
  import { useDataStyleStore } from "@ogw_front/stores/data_style"
9
10
  import { useViewerStore } from "@ogw_front/stores/viewer"
10
- import { setupIntegrationTests } from "@ogw_tests/integration/setup"
11
11
 
12
12
  // Local constants
13
13
  const INTERVAL_TIMEOUT = 25_000
@@ -3,11 +3,11 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
3
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" with { type: "json" }
4
4
 
5
5
  // Local imports
6
- import { cleanupBackend } from "@ogw_front/utils/local/microservices"
7
6
  import { Status } from "@ogw_front/utils/status"
7
+ import { cleanupBackend } from "@ogw_front/utils/local/microservices"
8
+ import { setupIntegrationTests } from "@ogw_tests/integration/setup"
8
9
  import { useDataStyleStore } from "@ogw_front/stores/data_style"
9
10
  import { useViewerStore } from "@ogw_front/stores/viewer"
10
- import { setupIntegrationTests } from "@ogw_tests/integration/setup"
11
11
 
12
12
  // Local constants
13
13
  const INTERVAL_TIMEOUT = 20_000
@@ -3,11 +3,11 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
3
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" with { type: "json" }
4
4
 
5
5
  // Local imports
6
- import { cleanupBackend } from "@ogw_front/utils/local/microservices"
7
6
  import { Status } from "@ogw_front/utils/status"
7
+ import { cleanupBackend } from "@ogw_front/utils/local/microservices"
8
+ import { setupIntegrationTests } from "@ogw_tests/integration/setup"
8
9
  import { useDataStyleStore } from "@ogw_front/stores/data_style"
9
10
  import { useViewerStore } from "@ogw_front/stores/viewer"
10
- import { setupIntegrationTests } from "@ogw_tests/integration/setup"
11
11
 
12
12
  // Local constants
13
13
  const INTERVAL_TIMEOUT = 20_000
@@ -3,11 +3,11 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
3
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" with { type: "json" }
4
4
 
5
5
  // Local imports
6
- import { cleanupBackend } from "@ogw_front/utils/local/microservices"
7
6
  import { Status } from "@ogw_front/utils/status"
7
+ import { cleanupBackend } from "@ogw_front/utils/local/microservices"
8
+ import { setupIntegrationTests } from "@ogw_tests/integration/setup"
8
9
  import { useDataStyleStore } from "@ogw_front/stores/data_style"
9
10
  import { useViewerStore } from "@ogw_front/stores/viewer"
10
- import { setupIntegrationTests } from "@ogw_tests/integration/setup"
11
11
 
12
12
  // Local constants
13
13
  const INTERVAL_TIMEOUT = 20_000
@@ -3,11 +3,11 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
3
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" with { type: "json" }
4
4
 
5
5
  // Local imports
6
- import { cleanupBackend } from "@ogw_front/utils/local/microservices"
7
6
  import { Status } from "@ogw_front/utils/status"
7
+ import { cleanupBackend } from "@ogw_front/utils/local/microservices"
8
+ import { setupIntegrationTests } from "@ogw_tests/integration/setup"
8
9
  import { useDataStyleStore } from "@ogw_front/stores/data_style"
9
10
  import { useViewerStore } from "@ogw_front/stores/viewer"
10
- import { setupIntegrationTests } from "@ogw_tests/integration/setup"
11
11
 
12
12
  // Local constants
13
13
  const INTERVAL_TIMEOUT = 20_000
@@ -3,12 +3,12 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
3
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" with { type: "json" }
4
4
 
5
5
  // Local imports
6
- import { cleanupBackend } from "@ogw_front/utils/local/microservices"
7
6
  import { Status } from "@ogw_front/utils/status"
7
+ import { cleanupBackend } from "@ogw_front/utils/local/microservices"
8
+ import { setupIntegrationTests } from "@ogw_tests/integration/setup"
8
9
  import { useDataStore } from "@ogw_front/stores/data"
9
10
  import { useDataStyleStore } from "@ogw_front/stores/data_style"
10
11
  import { useViewerStore } from "@ogw_front/stores/viewer"
11
- import { setupIntegrationTests } from "@ogw_tests/integration/setup"
12
12
 
13
13
  // Local constants
14
14
  const INTERVAL_TIMEOUT = 20_000
@@ -3,12 +3,12 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
3
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" with { type: "json" }
4
4
 
5
5
  // Local imports
6
- import { cleanupBackend } from "@ogw_front/utils/local/microservices"
7
6
  import { Status } from "@ogw_front/utils/status"
7
+ import { cleanupBackend } from "@ogw_front/utils/local/microservices"
8
+ import { setupIntegrationTests } from "@ogw_tests/integration/setup"
8
9
  import { useDataStore } from "@ogw_front/stores/data"
9
10
  import { useDataStyleStore } from "@ogw_front/stores/data_style"
10
11
  import { useViewerStore } from "@ogw_front/stores/viewer"
11
- import { setupIntegrationTests } from "@ogw_tests/integration/setup"
12
12
 
13
13
  // Local constants
14
14
  const INTERVAL_TIMEOUT = 20_000
@@ -3,11 +3,11 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
3
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" with { type: "json" }
4
4
 
5
5
  // Local imports
6
- import { cleanupBackend } from "@ogw_front/utils/local/microservices"
7
6
  import { Status } from "@ogw_front/utils/status"
7
+ import { cleanupBackend } from "@ogw_front/utils/local/microservices"
8
+ import { setupIntegrationTests } from "@ogw_tests/integration/setup"
8
9
  import { useDataStyleStore } from "@ogw_front/stores/data_style"
9
10
  import { useViewerStore } from "@ogw_front/stores/viewer"
10
- import { setupIntegrationTests } from "@ogw_tests/integration/setup"
11
11
 
12
12
  // Local constants
13
13
  const INTERVAL_TIMEOUT = 25_000
@@ -3,11 +3,11 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
3
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" with { type: "json" }
4
4
 
5
5
  // Local imports
6
- import { cleanupBackend } from "@ogw_front/utils/local/microservices"
7
6
  import { Status } from "@ogw_front/utils/status"
7
+ import { cleanupBackend } from "@ogw_front/utils/local/microservices"
8
+ import { setupIntegrationTests } from "@ogw_tests/integration/setup"
8
9
  import { useDataStyleStore } from "@ogw_front/stores/data_style"
9
10
  import { useViewerStore } from "@ogw_front/stores/viewer"
10
- import { setupIntegrationTests } from "@ogw_tests/integration/setup"
11
11
 
12
12
  // Local constants
13
13
  const INTERVAL_TIMEOUT = 20_000
@@ -3,12 +3,12 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
3
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" with { type: "json" }
4
4
 
5
5
  // Local imports
6
- import { cleanupBackend } from "@ogw_front/utils/local/microservices"
7
6
  import { Status } from "@ogw_front/utils/status"
7
+ import { cleanupBackend } from "@ogw_front/utils/local/microservices"
8
+ import { setupIntegrationTests } from "@ogw_tests/integration/setup"
8
9
  import { useDataStore } from "@ogw_front/stores/data"
9
10
  import { useDataStyleStore } from "@ogw_front/stores/data_style"
10
11
  import { useViewerStore } from "@ogw_front/stores/viewer"
11
- import { setupIntegrationTests } from "@ogw_tests/integration/setup"
12
12
 
13
13
  // Local constants
14
14
  const INTERVAL_TIMEOUT = 20_000
@@ -3,11 +3,11 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
3
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" with { type: "json" }
4
4
 
5
5
  // Local imports
6
- import { cleanupBackend } from "@ogw_front/utils/local/microservices"
7
6
  import { Status } from "@ogw_front/utils/status"
7
+ import { cleanupBackend } from "@ogw_front/utils/local/microservices"
8
+ import { setupIntegrationTests } from "@ogw_tests/integration/setup"
8
9
  import { useDataStyleStore } from "@ogw_front/stores/data_style"
9
10
  import { useViewerStore } from "@ogw_front/stores/viewer"
10
- import { setupIntegrationTests } from "@ogw_tests/integration/setup"
11
11
 
12
12
  // Local constants
13
13
  const INTERVAL_TIMEOUT = 20_000
@@ -3,12 +3,12 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"
3
3
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" with { type: "json" }
4
4
 
5
5
  // Local imports
6
- import { cleanupBackend } from "@ogw_front/utils/local/microservices"
7
6
  import { Status } from "@ogw_front/utils/status"
7
+ import { cleanupBackend } from "@ogw_front/utils/local/microservices"
8
+ import { setupIntegrationTests } from "@ogw_tests/integration/setup"
8
9
  import { useDataStore } from "@ogw_front/stores/data"
9
10
  import { useDataStyleStore } from "@ogw_front/stores/data_style"
10
11
  import { useViewerStore } from "@ogw_front/stores/viewer"
11
- import { setupIntegrationTests } from "@ogw_tests/integration/setup"
12
12
 
13
13
  // Local constants
14
14
  const INTERVAL_TIMEOUT = 20_000
@@ -5,11 +5,11 @@ import { afterEach, beforeEach, describe, expect, test } from "vitest"
5
5
  import opengeodeweb_viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json"
6
6
 
7
7
  // Local imports
8
- import { cleanupBackend } from "@ogw_front/utils/local/microservices"
9
8
  import { Status } from "@ogw_front/utils/status"
10
- import { useViewerStore } from "@ogw_front/stores/viewer"
9
+ import { cleanupBackend } from "@ogw_front/utils/local/microservices"
11
10
  import { runMicroservices } from "@ogw_tests/integration/setup"
12
11
  import { setupActivePinia } from "@ogw_tests/utils"
12
+ import { useViewerStore } from "@ogw_front/stores/viewer"
13
13
 
14
14
  const CONNECT_TIMEOUT = 25_000
15
15
 
@@ -275,12 +275,7 @@ describe("Infra Store", () => {
275
275
  })
276
276
 
277
277
  describe("create_backend", () => {
278
- // test("test without microservices", async () => {
279
- // const infraStore = useInfraStore()
280
- // infraStore.app_mode = appMode.BROWSER
281
- // await infraStore.create_backend()
282
- // expect(infraStore.status).toBe(Status.CREATED)
283
- // })
278
+ // Test without microservices
284
279
  test("test with end-point", async () => {
285
280
  const infraStore = useInfraStore()
286
281
  const geodeStore = useGeodeStore()