@geode/opengeodeweb-front 10.30.0-rc.8 → 10.30.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 (43) hide show
  1. package/app/components/ObjectSelector.vue +26 -76
  2. package/app/components/Viewer/Generic/Model/BlocksOptions.vue +2 -2
  3. package/app/components/Viewer/Generic/Model/CornersOptions.vue +2 -2
  4. package/app/components/Viewer/Generic/Model/LinesOptions.vue +2 -2
  5. package/app/components/Viewer/Generic/Model/SurfacesOptions.vue +2 -2
  6. package/app/components/Viewer/ObjectTree/Base/CommonTreeView.vue +1 -0
  7. package/app/components/Viewer/ObjectTree/Base/TreeRow.vue +8 -0
  8. package/app/components/Viewer/ObjectTree/Views/GlobalObjects.vue +3 -0
  9. package/app/components/Viewer/Options/AttributeSelector.vue +13 -6
  10. package/app/components/Viewer/Options/ColoringTypeSelector.vue +7 -7
  11. package/app/composables/project_manager.js +14 -11
  12. package/app/stores/app.js +8 -7
  13. package/app/stores/back.js +2 -13
  14. package/app/stores/cloud.js +3 -0
  15. package/app/stores/infra.js +0 -5
  16. package/app/stores/viewer.js +0 -12
  17. package/app/utils/extension.js +4 -3
  18. package/app/utils/import_workflow.js +1 -9
  19. package/app/utils/log.js +18 -0
  20. package/internal/utils/api_fetch.js +26 -53
  21. package/internal/utils/upload_file.js +33 -29
  22. package/internal/utils/viewer_call.js +7 -2
  23. package/package.json +17 -16
  24. package/server/api/cloud/extensions/run.post.js +72 -0
  25. package/server/api/{microservice → local}/extensions/run.post.js +10 -46
  26. package/server/api/microservice/app/set_back_base_url.post.js +22 -0
  27. package/server/api/microservice/app/set_viewer_base_url.post.js +22 -0
  28. package/server/utils/cloud.js +107 -1
  29. package/server/utils/extension.js +50 -0
  30. package/server/utils/microservices.js +51 -26
  31. package/server/utils/path.js +10 -0
  32. package/server/utils/server_config.js +4 -10
  33. package/shared/scripts.js +47 -9
  34. package/shared/utils/fetch_raw.js +1 -0
  35. package/shared/utils/response_handlers/load.js +74 -0
  36. package/tests/unit/composables/project_manager.nuxt.test.js +12 -1
  37. package/tests/unit/composables/upload_file.nuxt.test.js +2 -2
  38. package/tests/unit/stores/cloud.nuxt.test.js +23 -26
  39. package/tests/unit/stores/infra.nuxt.test.js +12 -5
  40. package/tests/unit/utils/validate_schema.nuxt.test.js +1 -1
  41. package/app/utils/validate_schema.js +0 -13
  42. /package/server/api/{microservice → local}/extensions/kill.post.js +0 -0
  43. /package/server/api/serverless/{run_cloud.js → run_cloud.post.js} +0 -0
@@ -1,79 +1,52 @@
1
- import _ from "lodash";
1
+ import { endRequestLog, startRequestLog } from "@ogw_front/utils/log";
2
+ import { fetchSchema } from "@ogw_shared/utils/fetch_schema";
2
3
  import { useFeedbackStore } from "@ogw_front/stores/feedback";
3
- import { validate_schema } from "@ogw_front/utils/validate_schema";
4
-
5
- const ERROR_400 = 400;
6
4
 
7
5
  export function api_fetch(
8
6
  microservice,
9
- { schema, params, headers },
7
+ { schema, params = {}, headers = {} },
10
8
  { request_error_function, response_function, response_error_function, timeout } = {},
11
9
  ) {
12
10
  console.log("[API] Fetching", microservice.base_url);
13
11
  const feedbackStore = useFeedbackStore();
14
-
15
- const body = params || {};
16
-
17
- const { valid, error: schema_error } = validate_schema(schema, body);
18
-
19
- if (!valid) {
20
- if (process.env.NODE_ENV !== "production") {
21
- console.log("Bad request", schema_error, schema, params);
22
- }
23
- feedbackStore.add_error(ERROR_400, schema.$id, "Bad request", schema_error);
24
- throw new Error(`${schema.$id}: ${schema_error}`);
25
- }
26
-
27
12
  microservice.start_request();
28
13
 
29
- const method = schema.methods.find((methodItem) => methodItem !== "OPTIONS");
30
- const request_options = {
31
- method,
32
- headers,
33
- };
34
- if (!_.isEmpty(body)) {
35
- request_options.body = body;
36
- }
37
-
38
- if (schema.max_retry) {
39
- request_options.max_retry = schema.max_retry;
40
- }
41
-
42
- function performFetch() {
43
- return $fetch(schema.$id, {
14
+ const requestStartingTime = startRequestLog(microservice, schema);
15
+ return fetchSchema(
16
+ {
17
+ schema,
44
18
  baseURL: microservice.base_url,
45
- ...request_options,
46
- onRequestError({ error }) {
19
+ params,
20
+ headers,
21
+ max_retry: schema.max_retry,
22
+ timeout,
23
+ },
24
+ {
25
+ request_error_function(error) {
47
26
  microservice.stop_request();
48
27
  feedbackStore.add_error(error.code, schema.$id, error.message, error.stack);
49
28
  if (request_error_function) {
50
29
  request_error_function(error);
51
30
  }
52
31
  },
53
- onResponse({ response }) {
54
- if (response.ok) {
55
- microservice.stop_request();
56
- if (response_function) {
57
- response_function(response._data);
58
- }
32
+ response_function(data) {
33
+ endRequestLog(microservice, schema, requestStartingTime);
34
+ microservice.stop_request();
35
+ if (response_function) {
36
+ response_function(data);
59
37
  }
60
38
  },
61
- onResponseError({ response }) {
39
+ response_error_function(response) {
62
40
  microservice.stop_request();
63
41
  feedbackStore.add_error(response.status, schema.$id, response.name, response.description);
64
42
  if (response_error_function) {
65
43
  response_error_function(response);
66
44
  }
67
45
  },
68
- });
69
- }
70
-
71
- if (timeout > 0) {
72
- return pTimeout(performCall(), {
73
- milliseconds: timeout,
74
- message: `${schema.$id}: Timed out after ${timeout}ms`,
75
- });
76
- }
77
-
78
- return performFetch();
46
+ validation_error_function({ code, name, error }) {
47
+ microservice.stop_request();
48
+ feedbackStore.add_error(code, schema.$id, name, error);
49
+ },
50
+ },
51
+ );
79
52
  }
@@ -1,14 +1,16 @@
1
+ import { fetchRaw } from "@ogw_shared/utils/fetch_raw.js";
1
2
  import { useFeedbackStore } from "@ogw_front/stores/feedback.js";
2
3
 
3
- async function upload_file(
4
+ function upload_file(
4
5
  microservice,
5
- { route, file, params = {} },
6
+ { schema, file, params = {} },
6
7
  { request_error_function, response_function, response_error_function } = {},
7
8
  ) {
8
- console.log("[UPLOAD_FILE] Uploading file", { route, file });
9
+ console.log("[UPLOAD_FILE] Uploading file", { schema, file });
9
10
  const feedbackStore = useFeedbackStore();
11
+
10
12
  if (!(file instanceof File)) {
11
- throw new Error("file must be a instance of File");
13
+ return Promise.reject(new Error("file must be an instance of File"));
12
14
  }
13
15
 
14
16
  const body = new FormData();
@@ -17,37 +19,39 @@ async function upload_file(
17
19
  }
18
20
  body.append("file", file);
19
21
 
20
- const request_options = {
21
- method: "PUT",
22
- body,
23
- };
24
22
  microservice.start_request();
25
- return await $fetch(route, {
26
- baseURL: microservice.base_url || "",
27
- ...request_options,
28
- onRequestError({ error }) {
29
- microservice.stop_request();
30
- feedbackStore.add_error(error.code, route, error.message, error.stack);
31
- if (request_error_function) {
32
- request_error_function(error);
33
- }
23
+ const route = schema.$id;
24
+
25
+ return fetchRaw(
26
+ {
27
+ route,
28
+ method: schema.methods.find((method) => method !== "OPTIONS"),
29
+ params: body,
30
+ baseURL: microservice.base_url,
34
31
  },
35
- onResponse({ response }) {
36
- if (response.ok) {
32
+ {
33
+ request_error_function(error) {
34
+ microservice.stop_request();
35
+ feedbackStore.add_error(error.code, route, error.message, error.stack);
36
+ if (request_error_function) {
37
+ request_error_function(error);
38
+ }
39
+ },
40
+ response_function(data) {
37
41
  microservice.stop_request();
38
42
  if (response_function) {
39
- response_function(response);
43
+ response_function(data);
40
44
  }
41
- }
42
- },
43
- onResponseError({ response }) {
44
- microservice.stop_request();
45
- feedbackStore.add_error(response.status, route, response.name, response.description);
46
- if (response_error_function) {
47
- response_error_function(response);
48
- }
45
+ },
46
+ response_error_function(response) {
47
+ microservice.stop_request();
48
+ feedbackStore.add_error(response.status, route, response.name, response.description);
49
+ if (response_error_function) {
50
+ response_error_function(response);
51
+ }
52
+ },
49
53
  },
50
- });
54
+ );
51
55
  }
52
56
 
53
57
  export { upload_file };
@@ -1,6 +1,10 @@
1
+ // Third party imports
1
2
  import pTimeout from "p-timeout";
3
+
4
+ // Local imports
5
+ import { endRequestLog, startRequestLog } from "@ogw_front/utils/log";
2
6
  import { useFeedbackStore } from "@ogw_front/stores/feedback";
3
- import { validate_schema } from "@ogw_front/utils/validate_schema";
7
+ import { validate_schema } from "@ogw_shared/utils/validate_schema";
4
8
 
5
9
  const ERROR_400 = 400;
6
10
 
@@ -28,9 +32,10 @@ export function viewer_call(
28
32
  return;
29
33
  }
30
34
  microservice.start_request();
31
-
35
+ const requestStart = startRequestLog(microservice, schema);
32
36
  try {
33
37
  const value = await client.getConnection().getSession().call(schema.$id, [params]);
38
+ endRequestLog(microservice, schema, requestStart);
34
39
  if (response_function) {
35
40
  await response_function(value);
36
41
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geode/opengeodeweb-front",
3
- "version": "10.30.0-rc.8",
3
+ "version": "10.30.0",
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,12 +34,12 @@
34
34
  "build": ""
35
35
  },
36
36
  "dependencies": {
37
- "@geode/opengeodeweb-back": "next",
38
- "@geode/opengeodeweb-viewer": "next",
37
+ "@geode/opengeodeweb-back": "latest",
38
+ "@geode/opengeodeweb-viewer": "latest",
39
39
  "@google-cloud/run": "3.2.0",
40
40
  "@kitware/vtk.js": "33.3.0",
41
41
  "@mdi/font": "7.4.47",
42
- "@pinia/nuxt": "0.11.3",
42
+ "@pinia/nuxt": "1.0.1",
43
43
  "@types/node": "22.15.3",
44
44
  "@vueuse/components": "13.1.0",
45
45
  "@vueuse/nuxt": "13.1.0",
@@ -54,37 +54,38 @@
54
54
  "js-file-download": "0.4.12",
55
55
  "jszip": "3.10.1",
56
56
  "node-stream-zip": "1.15.0",
57
- "nuxt": "4.2.2",
57
+ "nuxt": "4.5.1",
58
58
  "p-timeout": "7.0.1",
59
- "pinia": "3.0.4",
59
+ "pinia": "4.0.2",
60
60
  "rimraf": "6.1.3",
61
61
  "rxjs": "7.8.2",
62
62
  "sanitize-filename": "1.6.3",
63
63
  "sass": "1.87.0",
64
64
  "semver": "7.7.1",
65
+ "unstorage": "1.17.5",
65
66
  "uuid": "11.1.0",
66
67
  "vuetify": "3.12.5",
67
68
  "vuetify-nuxt-module": "0.18.7",
68
- "ws": "8.18.3",
69
+ "ws": "8.21.2",
69
70
  "wslink": "1.12.4"
70
71
  },
71
72
  "devDependencies": {
72
- "@nuxt/test-utils": "3.21.0",
73
- "@pinia/testing": "1.0.3",
74
- "@vitejs/plugin-vue": "6.0.4",
75
- "@vue/test-utils": "2.4.6",
73
+ "@nuxt/test-utils": "4.1.0",
74
+ "@pinia/testing": "2.0.1",
75
+ "@vitejs/plugin-vue": "6.0.8",
76
+ "@vue/test-utils": "2.4.11",
76
77
  "happy-dom": "20.0.11",
77
78
  "msw": "2.11.1",
78
79
  "playwright-core": "1.52.0",
79
80
  "resize-observer-polyfill": "1.5.1",
80
81
  "unplugin-auto-import": "20.0.0",
81
- "vite": "7.3.1",
82
- "vite-plugin-vuetify": "2.1.1",
83
- "vitest": "4.0.15",
84
- "vitest-environment-nuxt": "1.0.1",
82
+ "vite": "8.2.0",
83
+ "vite-plugin-vuetify": "2.1.3",
84
+ "vitest": "4.1.10",
85
+ "vitest-environment-nuxt": "2.0.0",
85
86
  "vitest-indexeddb": "0.0.1"
86
87
  },
87
88
  "overrides": {
88
- "vue": "latest"
89
+ "vue": "3.5.40"
89
90
  }
90
91
  }
@@ -0,0 +1,72 @@
1
+ // Node imports
2
+ import fs from "node:fs";
3
+
4
+ // Third party imports
5
+ import { createError, defineEventHandler, readBody } from "h3";
6
+
7
+ // Local imports
8
+ import {
9
+ addMicroserviceMetadatas,
10
+ runExtension,
11
+ } from "@geode/opengeodeweb-front/server/utils/microservices.js";
12
+ import {
13
+ extensionBackendPath,
14
+ extensionFolderPath,
15
+ } from "@geode/opengeodeweb-front/server/utils/path.js";
16
+ import {
17
+ readExtensionFrontend,
18
+ readExtensionMetadata,
19
+ } from "@geode/opengeodeweb-front/server/utils/extension.js";
20
+ import { extensionsConf } from "@geode/opengeodeweb-front/server/utils/app_config.js";
21
+ import { unzipFile } from "@geode/opengeodeweb-front/server/utils/server.js";
22
+
23
+ export default defineEventHandler(async (event) => {
24
+ try {
25
+ console.log("NITRO: runExtensions", event);
26
+ const { projectFolderPath, projectName } = await readBody(event);
27
+ const extensionsConfig = extensionsConf(projectName);
28
+ const extensionsArray = await Promise.all(
29
+ Object.keys(extensionsConfig).map(async (extensionId) => {
30
+ const extensionPath = extensionsConfig[extensionId].path;
31
+ const unzippedExtensionPath = await unzipFile(
32
+ extensionPath,
33
+ extensionFolderPath(projectFolderPath, extensionId),
34
+ );
35
+ const { id, name, version, backendExecutable, frontendFile } =
36
+ await readExtensionMetadata(unzippedExtensionPath);
37
+ const frontendContent = await readExtensionFrontend(
38
+ unzippedExtensionPath,
39
+ frontendFile,
40
+ id,
41
+ );
42
+ fs.chmodSync(extensionBackendPath(unzippedExtensionPath, backendExecutable), "755");
43
+ const port = await runExtension(id, backendExecutable, unzippedExtensionPath, {
44
+ projectFolderPath,
45
+ });
46
+ await addMicroserviceMetadatas(projectFolderPath, {
47
+ type: "back",
48
+ name,
49
+ port,
50
+ });
51
+ return {
52
+ id,
53
+ name,
54
+ version,
55
+ frontendContent,
56
+ port,
57
+ };
58
+ }),
59
+ );
60
+
61
+ return {
62
+ statusCode: 200,
63
+ extensionsArray,
64
+ };
65
+ } catch (error) {
66
+ console.error("Error running extensions:", error);
67
+ throw createError({
68
+ statusCode: 500,
69
+ statusMessage: error.message,
70
+ });
71
+ }
72
+ });
@@ -1,6 +1,5 @@
1
1
  // Node imports
2
2
  import fs from "node:fs";
3
- import path from "node:path";
4
3
 
5
4
  // Third party imports
6
5
  import { createError, defineEventHandler, readBody } from "h3";
@@ -11,20 +10,21 @@ import {
11
10
  runBack,
12
11
  } from "@geode/opengeodeweb-front/server/utils/microservices.js";
13
12
  import {
14
- executableName,
13
+ extensionBackendPath,
15
14
  extensionFolderPath,
16
- extensionFrontendPath,
17
15
  } from "@geode/opengeodeweb-front/server/utils/path.js";
16
+ import {
17
+ readExtensionFrontend,
18
+ readExtensionMetadata,
19
+ } from "@geode/opengeodeweb-front/server/utils/extension.js";
18
20
  import { extensionsConf } from "@geode/opengeodeweb-front/server/utils/app_config.js";
19
21
  import { unzipFile } from "@geode/opengeodeweb-front/server/utils/server.js";
20
22
 
21
23
  export default defineEventHandler(async (event) => {
22
24
  try {
23
25
  console.log("NITRO: runExtensions", event);
24
- const body = await readBody(event);
25
- const { projectFolderPath, projectName } = body;
26
+ const { projectFolderPath, projectName } = await readBody(event);
26
27
  const extensionsConfig = extensionsConf(projectName);
27
-
28
28
  const extensionsArray = await Promise.all(
29
29
  Object.keys(extensionsConfig).map(async (extensionId) => {
30
30
  const extensionPath = extensionsConfig[extensionId].path;
@@ -32,50 +32,14 @@ export default defineEventHandler(async (event) => {
32
32
  extensionPath,
33
33
  extensionFolderPath(projectFolderPath, extensionId),
34
34
  );
35
- const metadataPath = path.join(unzippedExtensionPath, "metadata.json");
36
- const metadataContent = await fs.promises.readFile(metadataPath, "utf8");
37
-
38
- if (!metadataContent) {
39
- throw createError({
40
- statusCode: 400,
41
- statusMessage: "Invalid extension file: missing metadata.json",
42
- });
43
- }
44
- const metadata = JSON.parse(metadataContent);
45
- console.log("runExtensions", { metadata });
46
-
47
- const { id, name, version, backendExecutable, frontendFile } = metadata;
48
- console.log("runExtensions", { id, name, version, backendExecutable });
49
-
50
- if (!frontendFile) {
51
- throw createError({
52
- statusCode: 400,
53
- statusMessage: "Invalid extension file: missing frontend JavaScript",
54
- });
55
- }
56
- if (!backendExecutable) {
57
- throw createError({
58
- statusCode: 400,
59
- statusMessage: "Invalid extension file: missing backend executable",
60
- });
61
- }
62
-
63
- const frontendFilePath = await extensionFrontendPath(
35
+ const { id, name, version, backendExecutable, frontendFile } =
36
+ await readExtensionMetadata(unzippedExtensionPath);
37
+ const frontendContent = await readExtensionFrontend(
64
38
  unzippedExtensionPath,
65
39
  frontendFile,
66
- path.resolve(),
67
40
  id,
68
41
  );
69
-
70
- console.log("runExtensions", { frontendFilePath });
71
- const frontendContent = await fs.promises.readFile(frontendFilePath, "utf8");
72
-
73
- const backendExecutablePath = path.join(
74
- unzippedExtensionPath,
75
- executableName(backendExecutable),
76
- );
77
- console.log("runExtensions", { backendExecutablePath });
78
- fs.chmodSync(backendExecutablePath, "755");
42
+ fs.chmodSync(extensionBackendPath(unzippedExtensionPath, backendExecutable), "755");
79
43
  const port = await runBack(backendExecutable, unzippedExtensionPath, {
80
44
  projectFolderPath,
81
45
  });
@@ -0,0 +1,22 @@
1
+ // Third party imports
2
+ import { createError, defineEventHandler, readBody } from "h3";
3
+
4
+ // Local imports
5
+ import { setBackBaseUrl } from "@geode/opengeodeweb-front/server/utils/server_config.js";
6
+
7
+ export default defineEventHandler(async (event) => {
8
+ try {
9
+ const { baseUrl } = await readBody(event);
10
+ if (!baseUrl) {
11
+ throw createError({ statusCode: 400, statusMessage: "baseUrl is required" });
12
+ }
13
+ await setBackBaseUrl(baseUrl);
14
+ return { statusCode: 200, baseUrl };
15
+ } catch (error) {
16
+ console.log(error);
17
+ throw createError({
18
+ statusCode: error.statusCode,
19
+ statusMessage: error.message,
20
+ });
21
+ }
22
+ });
@@ -0,0 +1,22 @@
1
+ // Third party imports
2
+ import { createError, defineEventHandler, readBody } from "h3";
3
+
4
+ // Local imports
5
+ import { setViewerBaseUrl } from "@geode/opengeodeweb-front/server/utils/server_config.js";
6
+
7
+ export default defineEventHandler(async (event) => {
8
+ try {
9
+ const { baseUrl } = await readBody(event);
10
+ if (!baseUrl) {
11
+ throw createError({ statusCode: 400, statusMessage: "baseUrl is required" });
12
+ }
13
+ await setViewerBaseUrl(baseUrl);
14
+ return { statusCode: 200, baseUrl };
15
+ } catch (error) {
16
+ console.log(error);
17
+ throw createError({
18
+ statusCode: error.statusCode,
19
+ statusMessage: error.message,
20
+ });
21
+ }
22
+ });
@@ -5,6 +5,8 @@ import { google } from "googleapis";
5
5
 
6
6
  // Local imports
7
7
 
8
+ const LOCATIONS_DIR = "/etc/nginx/locations";
9
+
8
10
  async function artifactImage(parent, authClient) {
9
11
  const projectName = process.env.PROJECT;
10
12
  const registry = google.artifactregistry({
@@ -92,4 +94,108 @@ function requestConfig(parent, image, email, projectName) {
92
94
  };
93
95
  }
94
96
 
95
- export { artifactImage, requestConfig };
97
+ function addSupervisorProgram(name, command, executableArgs) {
98
+ const conf = `
99
+ [program:${name}]
100
+ command=${command} ${executableArgs.join(" ")}
101
+ autostart=true
102
+ autorestart=true
103
+ stdout_logfile=/dev/stdout
104
+ stdout_logfile_maxbytes=0
105
+ stderr_logfile=/dev/stderr
106
+ stderr_logfile_maxbytes=0
107
+ `;
108
+ const confPath = path.join("/etc/supervisor/conf.d", `${name}.conf`);
109
+ fs.writeFileSync(confPath, conf);
110
+ execFileSync("supervisorctl", ["reread"]);
111
+ execFileSync("supervisorctl", ["update"]);
112
+ const stdout = execFileSync("supervisorctl", ["start", name]);
113
+ console.log("addSupervisorProgram", stdout);
114
+ }
115
+
116
+ function buildLocationBlock(routePath, port) {
117
+ if (!routePath.startsWith("/") || !routePath.endsWith("/")) {
118
+ throw new Error(`routePath must start and end with '/', got: ${routePath}`);
119
+ }
120
+ const methods = "GET, POST, PUT, DELETE, PATCH, OPTIONS";
121
+ const limitMethods = methods
122
+ .split(",")
123
+ .map((method) => method.trim())
124
+ .filter((method) => method !== "OPTIONS")
125
+ .join(" ");
126
+
127
+ return `# ====================== ${routePath} location ======================
128
+ location ~ "^${routePath}" {
129
+ if ($request_method = 'OPTIONS') {
130
+ add_header 'Access-Control-Allow-Origin' $allow_origin always;
131
+ add_header 'Access-Control-Allow-Credentials' 'true' always;
132
+ add_header 'Access-Control-Allow-Methods' '${methods}' always;
133
+ add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,X-CSRF-Token' always;
134
+ add_header 'Access-Control-Max-Age' 1728000 always;
135
+ add_header 'Content-Type' 'text/plain; charset=utf-8';
136
+ add_header 'Content-Length' 0;
137
+ return 204;
138
+ }
139
+
140
+ limit_except ${limitMethods} { deny all; }
141
+
142
+ add_header 'Access-Control-Allow-Origin' $allow_origin always;
143
+ add_header 'Access-Control-Allow-Credentials' 'true' always;
144
+ add_header 'Access-Control-Allow-Methods' '${methods}' always;
145
+ add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,X-CSRF-Token' always;
146
+ add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
147
+ add_header 'Vary' 'Origin' always;
148
+
149
+ rewrite "^${routePath}(.*)" /$1 break;
150
+ proxy_pass http://localhost:${port};
151
+ proxy_http_version 1.1;
152
+ proxy_set_header Host $host;
153
+ proxy_set_header X-Real-IP $remote_addr;
154
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
155
+ proxy_set_header X-Forwarded-Proto $scheme;
156
+ }
157
+ `;
158
+ }
159
+
160
+ function nginxConfigFile(name) {
161
+ return path.join(LOCATIONS_DIR, `${name}.conf`);
162
+ }
163
+
164
+ function nginxReload() {
165
+ execFileSync("nginx", ["-t"]);
166
+ execFileSync("nginx", ["-s", "reload"]);
167
+ }
168
+
169
+ function addNginxLocation(name, port) {
170
+ fs.mkdirSync(LOCATIONS_DIR, { recursive: true });
171
+ const filePath = nginxConfigFile(name);
172
+ if (fs.existsSync(filePath)) {
173
+ throw new Error(`Location '${name}' already exists at ${filePath}`);
174
+ }
175
+ fs.writeFileSync(filePath, buildLocationBlock(`/${name}/`, port));
176
+ try {
177
+ nginxReload();
178
+ } catch (error) {
179
+ fs.unlinkSync(filePath);
180
+ throw error;
181
+ }
182
+ return filePath;
183
+ }
184
+
185
+ function removeNginxLocation(name) {
186
+ const filePath = filePathFor(name);
187
+ if (!fs.existsSync(filePath)) {
188
+ return false;
189
+ }
190
+ fs.unlinkSync(filePath);
191
+ nginxReload();
192
+ return true;
193
+ }
194
+
195
+ export {
196
+ addNginxLocation,
197
+ addSupervisorProgram,
198
+ artifactImage,
199
+ removeNginxLocation,
200
+ requestConfig,
201
+ };
@@ -0,0 +1,50 @@
1
+ // Node imports
2
+ import { promises as fs } from "node:fs";
3
+ import path from "node:path";
4
+
5
+ // Third party imports
6
+ import { createError } from "h3";
7
+
8
+ // Local imports
9
+ import { extensionFrontendPath } from "@geode/opengeodeweb-front/server/utils/path.js";
10
+
11
+ async function readExtensionMetadata(unzippedExtensionPath) {
12
+ const metadataPath = path.join(unzippedExtensionPath, "metadata.json");
13
+ const metadataContent = await fs.readFile(metadataPath, "utf8");
14
+ if (!metadataContent) {
15
+ throw createError({
16
+ statusCode: 400,
17
+ statusMessage: "Invalid extension file: missing metadata.json",
18
+ });
19
+ }
20
+ const metadata = JSON.parse(metadataContent);
21
+ console.log("readExtensionMetadata", { metadata });
22
+ if (!metadata.frontendFile) {
23
+ throw createError({
24
+ statusCode: 400,
25
+ statusMessage: "Invalid extension file: missing frontend JavaScript",
26
+ });
27
+ }
28
+ if (!metadata.backendExecutable) {
29
+ throw createError({
30
+ statusCode: 400,
31
+ statusMessage: "Invalid extension file: missing backend executable",
32
+ });
33
+ }
34
+
35
+ return metadata;
36
+ }
37
+
38
+ async function readExtensionFrontend(unzippedExtensionPath, frontendFile, id) {
39
+ console.log("readExtensionFrontend", { id });
40
+ const frontendFilePath = await extensionFrontendPath(
41
+ unzippedExtensionPath,
42
+ frontendFile,
43
+ path.resolve(),
44
+ id,
45
+ );
46
+ console.log("readExtensionFrontend", { frontendFilePath });
47
+ return fs.readFile(frontendFilePath, "utf8");
48
+ }
49
+
50
+ export { readExtensionFrontend, readExtensionMetadata };