@geode/opengeodeweb-front 10.24.2 → 10.25.0-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.
@@ -128,7 +128,7 @@ const polyhedron_attribute_color_map = computed({
128
128
  v-model:polyhedron_attribute_range="polyhedron_attribute_range"
129
129
  v-model:polyhedron_attribute_color_map="polyhedron_attribute_color_map"
130
130
  :capabilities="{
131
- vertex: { available: true, hasColorMap: false },
131
+ vertex: { available: true, hasColorMap: true },
132
132
  polyhedron: { available: true, hasColorMap: true },
133
133
  }"
134
134
  />
package/app/stores/app.js CHANGED
@@ -2,6 +2,9 @@
2
2
  import { api_fetch } from "@ogw_internal/utils/api_fetch.js";
3
3
  import { upload_file } from "@ogw_internal/utils/upload_file.js";
4
4
 
5
+ import { killExtension } from "@ogw_front/utils/extension.js";
6
+ import { useInfraStore } from "@ogw_front/stores/infra";
7
+
5
8
  // oxlint-disable-next-line max-lines-per-function, max-statements
6
9
  export const useAppStore = defineStore("app", () => {
7
10
  const stores = [];
@@ -155,51 +158,41 @@ export const useAppStore = defineStore("app", () => {
155
158
  return [...loadedExtensions.value.values()];
156
159
  }
157
160
 
158
- function unloadExtension(id) {
159
- const extensionData = getExtension(id);
160
- if (!extensionData) {
161
- return false;
162
- }
163
- if (extensionData.module && typeof extensionData.module.uninstall === "function") {
164
- try {
165
- extensionData.module.uninstall(extensionAPI.value);
166
- console.log(`[AppStore] Extension uninstall called: ${id}`);
167
- } catch (error) {
168
- console.error(`[AppStore] Error calling uninstall for ${id}:`, error);
169
- }
170
- }
171
-
172
- if (extensionAPI.value && typeof extensionAPI.value.unregisterToolsByExtension === "function") {
173
- extensionAPI.value.unregisterToolsByExtension(id);
174
- }
161
+ async function unloadExtension(extensionId) {
162
+ console.log(`[AppStore] Unloading extension: ${extensionId}`);
163
+ const infraStore = useInfraStore();
164
+ await infraStore.unregister_microservice(extensionId);
165
+ await killExtension(extensionId);
175
166
 
176
- loadedExtensions.value.delete(id);
177
- console.log(`[AppStore] Extension unloaded: ${id}`);
167
+ loadedExtensions.value.delete(extensionId);
168
+ console.log(`[AppStore] Extension unloaded: ${extensionId}`);
178
169
  return true;
179
170
  }
180
171
 
181
- function toggleExtension(id) {
182
- const extensionData = getExtension(id);
172
+ function toggleExtension(extensionId) {
173
+ const extensionData = getExtension(extensionId);
183
174
  if (!extensionData) {
184
175
  return false;
185
176
  }
186
177
  extensionData.enabled = !extensionData.enabled;
187
- console.log(`[AppStore] Extension ${extensionData.enabled ? "enabled" : "disabled"}: ${id}`);
178
+ console.log(
179
+ `[AppStore] Extension ${extensionData.enabled ? "enabled" : "disabled"}: ${extensionId}`,
180
+ );
188
181
  return extensionData.enabled;
189
182
  }
190
183
 
191
- function setExtensionEnabled(id, enabled) {
192
- const extensionData = getExtension(id);
184
+ function setExtensionEnabled(extensionId, enabled) {
185
+ const extensionData = getExtension(extensionId);
193
186
  if (!extensionData) {
194
187
  return false;
195
188
  }
196
189
  extensionData.enabled = enabled;
197
- console.log(`[AppStore] Extension ${enabled ? "enabled" : "disabled"}: ${id}`);
190
+ console.log(`[AppStore] Extension ${enabled ? "enabled" : "disabled"}: ${extensionId}`);
198
191
  return true;
199
192
  }
200
193
 
201
- function getExtensionEnabled(id) {
202
- return getExtension(id)?.enabled ?? false;
194
+ function getExtensionEnabled(extensionId) {
195
+ return getExtension(extensionId)?.enabled ?? false;
203
196
  }
204
197
 
205
198
  function upload(file, callbacks = {}) {
@@ -30,6 +30,13 @@ export const useInfraStore = defineStore("infra", {
30
30
  console.log("[INFRA] Microservice registered:", store_name);
31
31
  }
32
32
  },
33
+ unregister_microservice(microserviceId) {
34
+ console.log("[INFRA] Unregistering microservice:", microserviceId);
35
+ this.microservices = this.microservices.filter(
36
+ (microservice) => microservice.$id !== microserviceId,
37
+ );
38
+ console.log("[INFRA] Microservice unregistered:", microserviceId);
39
+ },
33
40
  create_backend(name, email, launch) {
34
41
  console.log("[INFRA] Starting create_backend - Mode:", this.app_mode);
35
42
  console.log(
@@ -1,5 +1,6 @@
1
1
  // Node.js imports
2
2
  import path from "node:path";
3
+ import { unlink } from "node:fs";
3
4
 
4
5
  // Third party imports
5
6
  import Conf from "conf";
@@ -31,9 +32,30 @@ function addExtensionToConf(projectName, { extensionID, extensionPath }) {
31
32
  projectConfig.set(`extensions.${extensionID}.path`, extensionPath);
32
33
  }
33
34
 
35
+ async function removeExtensionFromConf(projectName, extensionID) {
36
+ const projectConfig = projectConf(projectName);
37
+ const extensionArchivePath = extensionPathFromConf(projectName, extensionID);
38
+
39
+ await unlink(extensionArchivePath, (error) => {
40
+ if (error) {
41
+ throw error;
42
+ }
43
+ console.log(`${extensionArchivePath} was deleted`);
44
+ });
45
+ projectConfig.delete(`extensions.${extensionID}`);
46
+ console.log(`${extensionID} was deleted from ${projectName} config`);
47
+ }
48
+
34
49
  function extensionPathFromConf(projectName, extensionID) {
35
50
  const projectConfig = projectConf(projectName);
36
51
  return projectConfig.get(`extensions.${extensionID}.path`);
37
52
  }
38
53
 
39
- export { confFolderPath, projectConf, extensionsConf, addExtensionToConf, extensionPathFromConf };
54
+ export {
55
+ confFolderPath,
56
+ projectConf,
57
+ extensionsConf,
58
+ addExtensionToConf,
59
+ removeExtensionFromConf,
60
+ extensionPathFromConf,
61
+ };
@@ -95,10 +95,35 @@ function runExtensions() {
95
95
  return appStore.request({ schema, params });
96
96
  }
97
97
 
98
+ function killExtension(extensionId) {
99
+ const appStore = useAppStore();
100
+ const { projectFolderPath } = appStore;
101
+ const { PROJECT: projectName } = useRuntimeConfig().public;
102
+ const params = { extensionId, projectFolderPath, projectName };
103
+
104
+ console.log(`[AppStore] Killing extension: ${extensionId}`, { params });
105
+
106
+ const schema = {
107
+ $id: "/api/extensions/kill",
108
+ methods: ["POST"],
109
+ type: "object",
110
+ properties: {
111
+ extensionId: { type: "string" },
112
+ projectFolderPath: { type: "string" },
113
+ projectName: { type: "string" },
114
+ },
115
+ required: ["extensionId", "projectFolderPath", "projectName"],
116
+ additionalProperties: false,
117
+ };
118
+
119
+ return appStore.request({ schema, params });
120
+ }
121
+
98
122
  export {
99
123
  importExtensionFile,
100
124
  unloadExtension,
101
125
  uploadExtension,
102
126
  registerRunningExtensions,
103
127
  runExtensions,
128
+ killExtension,
104
129
  };
@@ -38,14 +38,9 @@ function killHttpMicroservice(microservice) {
38
38
  console.log("killHttpMicroservice", { ...microservice });
39
39
  const failMessage = `Failed to kill ${microservice.name}`;
40
40
  async function do_kill() {
41
- try {
42
- await fetch(microservice.url, {
43
- method: microservice.method,
44
- });
45
- throw new Error(failMessage);
46
- } catch (error) {
47
- console.log(`${microservice.name} closed`, error);
48
- }
41
+ await $fetch(microservice.url, {
42
+ method: microservice.method,
43
+ });
49
44
  }
50
45
  return pTimeout(do_kill(), {
51
46
  milliseconds: 5000,
@@ -103,20 +98,27 @@ function killWebsocketMicroservice(microservice) {
103
98
  });
104
99
  }
105
100
 
106
- function killMicroservice(microservice) {
101
+ async function killMicroservice(microservice, microservices) {
107
102
  if (microservice.type === "back") {
108
- return killHttpMicroservice(microservice);
103
+ await killHttpMicroservice(microservice);
104
+ } else if (microservice.type === "viewer") {
105
+ await killWebsocketMicroservice(microservice);
106
+ } else {
107
+ throw new Error(`Unknown microservice type: ${microservice.type}`);
109
108
  }
110
- if (microservice.type === "viewer") {
111
- return killWebsocketMicroservice(microservice);
109
+
110
+ if (microservices) {
111
+ const index = microservices.indexOf(microservice);
112
+ if (index !== -1) {
113
+ microservices.splice(index, 1);
114
+ }
112
115
  }
113
- throw new Error(`Unknown microservice type: ${microservice.type}`);
114
116
  }
115
117
 
116
118
  function killMicroservices(microservices) {
117
119
  console.log("killMicroservices", { microservices });
118
120
  return Promise.all(
119
- microservices.map(async (microservice) => await killMicroservice(microservice)),
121
+ microservices.map((microservice) => killMicroservice(microservice, microservices)),
120
122
  );
121
123
  }
122
124
 
@@ -142,8 +144,19 @@ async function cleanupBackend(projectFolderPath) {
142
144
  await deleteFolderRecursive(projectFolderPath);
143
145
  }
144
146
 
147
+ function getMicroserviceByName(microservices, name) {
148
+ return microservices.find((microservice) => microservice.name === name);
149
+ }
150
+
145
151
  function microservicesMetadatasPath(projectFolderPath) {
146
152
  return path.join(projectFolderPath, "microservices.json");
147
153
  }
148
154
 
149
- export { cleanupBackend, microservicesMetadatasPath, projectMicroservices };
155
+ export {
156
+ cleanupBackend,
157
+ deleteFolderRecursive,
158
+ killMicroservice,
159
+ microservicesMetadatasPath,
160
+ projectMicroservices,
161
+ getMicroserviceByName,
162
+ };
@@ -49,6 +49,10 @@ function generateProjectFolderPath(projectName) {
49
49
  return path.join(os.tmpdir(), projectName.replaceAll("/", "_"), uuidv4());
50
50
  }
51
51
 
52
+ function extensionFolderPath(projectFolderPath, extensionID) {
53
+ return path.join(projectFolderPath, "extensions", extensionID);
54
+ }
55
+
52
56
  async function lookForLocalExtensionDistPath(rootPath, extentionRepoName, frontendFile) {
53
57
  const localExtensionPath = path.join(rootPath, "..", extentionRepoName);
54
58
  const localExtensionDistPath = path.join(localExtensionPath, "dist");
@@ -108,6 +112,7 @@ async function extensionFrontendPath(unzippedExtensionPath, frontendFile, rootPa
108
112
  export {
109
113
  createPath,
110
114
  extensionFrontendPath,
115
+ extensionFolderPath,
111
116
  executablePath,
112
117
  executableName,
113
118
  generateProjectFolderPath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geode/opengeodeweb-front",
3
- "version": "10.24.2",
3
+ "version": "10.25.0-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": {
@@ -34,8 +34,8 @@
34
34
  "build": ""
35
35
  },
36
36
  "dependencies": {
37
- "@geode/opengeodeweb-back": "latest",
38
- "@geode/opengeodeweb-viewer": "latest",
37
+ "@geode/opengeodeweb-back": "next",
38
+ "@geode/opengeodeweb-viewer": "next",
39
39
  "@google-cloud/run": "3.2.0",
40
40
  "@kitware/vtk.js": "33.3.0",
41
41
  "@mdi/font": "7.4.47",
@@ -0,0 +1,43 @@
1
+ // Node imports
2
+
3
+ // Third party imports
4
+ import { createError, defineEventHandler, readBody } from "h3";
5
+
6
+ // Local imports
7
+ import {
8
+ deleteFolderRecursive,
9
+ getMicroserviceByName,
10
+ killMicroservice,
11
+ projectMicroservices,
12
+ } from "@geode/opengeodeweb-front/app/utils/local/cleanup.js";
13
+ import { extensionFolderPath } from "@geode/opengeodeweb-front/app/utils/local/path.js";
14
+ import { removeExtensionFromConf } from "@geode/opengeodeweb-front/app/utils/config.js";
15
+
16
+ export default defineEventHandler(async (event) => {
17
+ try {
18
+ const body = await readBody(event);
19
+ const { projectFolderPath, projectName, extensionId } = body;
20
+
21
+ console.log({ projectFolderPath, projectName, extensionId });
22
+
23
+ const microservices = projectMicroservices(projectFolderPath);
24
+ const microservice = getMicroserviceByName(microservices, extensionId);
25
+ if (!microservice) {
26
+ throw new Error(`Microservice ${extensionId} not found`);
27
+ }
28
+
29
+ await removeExtensionFromConf(projectName, extensionId);
30
+ await deleteFolderRecursive(extensionFolderPath(projectFolderPath, extensionId));
31
+ await killMicroservice(microservice, microservices);
32
+
33
+ return {
34
+ statusCode: 200,
35
+ };
36
+ } catch (error) {
37
+ console.error("Error killing extension:", error);
38
+ throw createError({
39
+ statusCode: 500,
40
+ statusMessage: error.message,
41
+ });
42
+ }
43
+ });
@@ -12,13 +12,12 @@ import {
12
12
  } from "@geode/opengeodeweb-front/app/utils/local/microservices.js";
13
13
  import {
14
14
  executableName,
15
+ extensionFolderPath,
15
16
  extensionFrontendPath,
16
17
  } from "@geode/opengeodeweb-front/app/utils/local/path.js";
17
18
  import { extensionsConf } from "@geode/opengeodeweb-front/app/utils/config.js";
18
19
  import { unzipFile } from "@geode/opengeodeweb-front/app/utils/server.js";
19
20
 
20
- const CODE_200 = 200;
21
-
22
21
  export default defineEventHandler(async (event) => {
23
22
  try {
24
23
  const body = await readBody(event);
@@ -30,7 +29,7 @@ export default defineEventHandler(async (event) => {
30
29
  const extensionPath = extensionsConfig[extensionID].path;
31
30
  const unzippedExtensionPath = await unzipFile(
32
31
  extensionPath,
33
- path.join(projectFolderPath, "extensions"),
32
+ extensionFolderPath(projectFolderPath, extensionID),
34
33
  );
35
34
  const metadataPath = path.join(unzippedExtensionPath, "metadata.json");
36
35
  const metadataContent = await fs.promises.readFile(metadataPath, "utf8");
@@ -95,7 +94,7 @@ export default defineEventHandler(async (event) => {
95
94
  );
96
95
 
97
96
  return {
98
- statusCode: CODE_200,
97
+ statusCode: 200,
99
98
  extensionsArray,
100
99
  };
101
100
  } catch (error) {