@geode/opengeodeweb-front 10.30.0-rc.15 → 10.30.0-rc.16
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.
- package/app/utils/extension.js +4 -3
- package/package.json +1 -1
- package/server/api/cloud/extensions/run.post.js +72 -0
- package/server/api/{microservice → local}/extensions/run.post.js +10 -46
- package/server/utils/cloud.js +107 -1
- package/server/utils/extension.js +50 -0
- package/server/utils/microservices.js +51 -26
- package/server/utils/path.js +10 -0
- /package/server/api/{microservice → local}/extensions/kill.post.js +0 -0
package/app/utils/extension.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import _ from "lodash";
|
|
5
5
|
|
|
6
6
|
// Local imports
|
|
7
|
+
import { isCloudMode } from "@ogw_front/utils/stores";
|
|
7
8
|
import { useAppStore } from "@ogw_front/stores/app";
|
|
8
9
|
import { useInfraStore } from "@ogw_front/stores/infra";
|
|
9
10
|
|
|
@@ -105,9 +106,9 @@ function runExtensions() {
|
|
|
105
106
|
const { projectFolderPath } = appStore;
|
|
106
107
|
const { PROJECT: projectName } = useRuntimeConfig().public;
|
|
107
108
|
const params = { projectFolderPath, projectName };
|
|
108
|
-
|
|
109
|
+
const endpoint = isCloudMode() ? "cloud" : "local";
|
|
109
110
|
const schema = {
|
|
110
|
-
$id:
|
|
111
|
+
$id: `/api/${endpoint}/extensions/run`,
|
|
111
112
|
methods: ["POST"],
|
|
112
113
|
type: "object",
|
|
113
114
|
properties: {
|
|
@@ -130,7 +131,7 @@ function killExtension(extensionId) {
|
|
|
130
131
|
console.log(`[AppStore] Killing extension: ${extensionId}`, { params });
|
|
131
132
|
|
|
132
133
|
const schema = {
|
|
133
|
-
$id: "/api/
|
|
134
|
+
$id: "/api/local/extensions/kill",
|
|
134
135
|
methods: ["POST"],
|
|
135
136
|
type: "object",
|
|
136
137
|
properties: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geode/opengeodeweb-front",
|
|
3
|
-
"version": "10.30.0-rc.
|
|
3
|
+
"version": "10.30.0-rc.16",
|
|
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": {
|
|
@@ -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
|
-
|
|
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
|
|
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
|
|
36
|
-
|
|
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
|
});
|
package/server/utils/cloud.js
CHANGED
|
@@ -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
|
-
|
|
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 };
|
|
@@ -7,6 +7,7 @@ import path from "node:path";
|
|
|
7
7
|
import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json" with { type: "json" };
|
|
8
8
|
|
|
9
9
|
// Local imports
|
|
10
|
+
import { addNginxLocation, addSupervisorProgram } from "./cloud.js";
|
|
10
11
|
import { getAvailablePort, waitForReady } from "./scripts.js";
|
|
11
12
|
import { microservicesMetadatasPath, projectMicroservices } from "./cleanup.js";
|
|
12
13
|
import { executablePath } from "./path.js";
|
|
@@ -57,33 +58,11 @@ function isPortInUseError(errorMessage) {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
async function runBack(execName, execPath, args = {}, attempts = 0) {
|
|
60
|
-
const { projectFolderPath } = args;
|
|
61
|
-
if (!projectFolderPath) {
|
|
62
|
-
throw new Error("projectFolderPath is required");
|
|
63
|
-
}
|
|
64
|
-
let { uploadFolderPath } = args;
|
|
65
|
-
if (!uploadFolderPath) {
|
|
66
|
-
uploadFolderPath = path.join(projectFolderPath, "uploads");
|
|
67
|
-
}
|
|
68
61
|
try {
|
|
69
62
|
const port = await getAvailablePort();
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"--project_folder_path",
|
|
74
|
-
projectFolderPath,
|
|
75
|
-
"--upload_folder_path",
|
|
76
|
-
uploadFolderPath,
|
|
77
|
-
"--allowed_origins",
|
|
78
|
-
"http://localhost:*",
|
|
79
|
-
"--timeout",
|
|
80
|
-
"0",
|
|
81
|
-
];
|
|
82
|
-
if (process.env.NODE_ENV === "development" || !process.env.NODE_ENV) {
|
|
83
|
-
backArgs.push("--debug");
|
|
84
|
-
}
|
|
85
|
-
console.log("runBack", execPath, execName, backArgs);
|
|
86
|
-
await runScript(execPath, execName, backArgs, "Serving Flask app");
|
|
63
|
+
const executableArgs = backArgs(args, port);
|
|
64
|
+
console.log("runBack", execPath, execName, executableArgs);
|
|
65
|
+
await runScript(execPath, execName, executableArgs, "Serving Flask app");
|
|
87
66
|
return port;
|
|
88
67
|
} catch (error) {
|
|
89
68
|
if (!isPortInUseError(error)) {
|
|
@@ -129,6 +108,52 @@ async function runViewer(execName, execPath, args = {}, attempts = 0) {
|
|
|
129
108
|
}
|
|
130
109
|
}
|
|
131
110
|
|
|
111
|
+
function backArgs(args, port) {
|
|
112
|
+
const { projectFolderPath } = args;
|
|
113
|
+
if (!projectFolderPath) {
|
|
114
|
+
throw new Error("projectFolderPath is required");
|
|
115
|
+
}
|
|
116
|
+
const uploadFolderPath = args.uploadFolderPath || path.join(projectFolderPath, "uploads");
|
|
117
|
+
const executableArgs = [
|
|
118
|
+
"--port",
|
|
119
|
+
String(port),
|
|
120
|
+
"--project_folder_path",
|
|
121
|
+
projectFolderPath,
|
|
122
|
+
"--upload_folder_path",
|
|
123
|
+
uploadFolderPath,
|
|
124
|
+
"--allowed_origins",
|
|
125
|
+
"http://localhost:*",
|
|
126
|
+
"--timeout",
|
|
127
|
+
"0",
|
|
128
|
+
];
|
|
129
|
+
if (process.env.NODE_ENV === "development" || !process.env.NODE_ENV) {
|
|
130
|
+
executableArgs.push("--debug");
|
|
131
|
+
}
|
|
132
|
+
return executableArgs;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function runExtension(extensionId, execName, execPath, args = {}, attempts = 0) {
|
|
136
|
+
try {
|
|
137
|
+
const port = await getAvailablePort();
|
|
138
|
+
const executableArgs = backArgs(args, port);
|
|
139
|
+
const command = executablePath(execPath, execName);
|
|
140
|
+
console.log("runExtension", execPath, execName, executableArgs);
|
|
141
|
+
addSupervisorProgram(extensionId, command, executableArgs);
|
|
142
|
+
addNginxLocation(extensionId, port);
|
|
143
|
+
return port;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
if (!isPortInUseError(error)) {
|
|
146
|
+
console.log("runBack error", error);
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
if (attempts <= MAX_PORT_RETRIES) {
|
|
150
|
+
console.log("Retrying runExtension on conflicting port", port);
|
|
151
|
+
const port = await runExtension(extensionId, execName, execPath, args, attempts + 1);
|
|
152
|
+
return port;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
132
157
|
function addMicroserviceMetadatas(projectFolderPath, serviceObj) {
|
|
133
158
|
const microservices = projectMicroservices(projectFolderPath);
|
|
134
159
|
if (serviceObj.type === "back") {
|
|
@@ -147,4 +172,4 @@ function addMicroserviceMetadatas(projectFolderPath, serviceObj) {
|
|
|
147
172
|
);
|
|
148
173
|
}
|
|
149
174
|
|
|
150
|
-
export { addMicroserviceMetadatas, runBack, runViewer };
|
|
175
|
+
export { addMicroserviceMetadatas, runBack, runExtension, runViewer };
|
package/server/utils/path.js
CHANGED
|
@@ -138,8 +138,18 @@ async function extensionFrontendPath(unzippedExtensionPath, frontendFile, rootPa
|
|
|
138
138
|
throw new Error(`Failed to find ${unzippedfrontendFilePath}`);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
function extensionBackendPath(unzippedExtensionPath, backendExecutableName) {
|
|
142
|
+
const backendExecutablePath = path.join(
|
|
143
|
+
unzippedExtensionPath,
|
|
144
|
+
executableName(backendExecutableName),
|
|
145
|
+
);
|
|
146
|
+
console.log("runExtensions", { backendExecutablePath });
|
|
147
|
+
return backendExecutablePath;
|
|
148
|
+
}
|
|
149
|
+
|
|
141
150
|
export {
|
|
142
151
|
createPath,
|
|
152
|
+
extensionBackendPath,
|
|
143
153
|
extensionFrontendPath,
|
|
144
154
|
extensionFolderPath,
|
|
145
155
|
executablePath,
|
|
File without changes
|