underpost 3.1.2 → 3.2.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.
- package/.env.example +0 -2
- package/.github/workflows/ghpkg.ci.yml +4 -4
- package/.github/workflows/npmpkg.ci.yml +38 -7
- package/.github/workflows/pwa-microservices-template-page.cd.yml +3 -4
- package/.github/workflows/pwa-microservices-template-test.ci.yml +3 -3
- package/.github/workflows/release.cd.yml +4 -4
- package/CHANGELOG.md +365 -1
- package/CLI-HELP.md +55 -3
- package/README.md +7 -3
- package/bin/build.js +18 -12
- package/bin/deploy.js +205 -225
- package/bin/file.js +3 -0
- package/conf.js +4 -10
- package/jsdoc.json +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +72 -50
- package/manifests/deployment/dd-test-development/proxy.yaml +13 -4
- package/manifests/deployment/playwright/deployment.yaml +1 -1
- package/nodemon.json +1 -1
- package/package.json +21 -14
- package/scripts/ports-ls.sh +2 -0
- package/scripts/rhel-grpc-setup.sh +56 -0
- package/src/api/file/file.ref.json +18 -0
- package/src/api/user/user.service.js +8 -7
- package/src/cli/cluster.js +7 -7
- package/src/cli/db.js +76 -242
- package/src/cli/deploy.js +104 -65
- package/src/cli/env.js +1 -0
- package/src/cli/fs.js +2 -1
- package/src/cli/index.js +50 -1
- package/src/cli/kubectl.js +211 -0
- package/src/cli/release.js +284 -0
- package/src/cli/repository.js +328 -112
- package/src/cli/run.js +283 -69
- package/src/cli/test.js +3 -3
- package/src/client/Default.index.js +3 -4
- package/src/client/components/core/Alert.js +2 -2
- package/src/client/components/core/AppStore.js +69 -0
- package/src/client/components/core/CalendarCore.js +2 -2
- package/src/client/components/core/Docs.js +9 -2
- package/src/client/components/core/DropDown.js +129 -17
- package/src/client/components/core/Keyboard.js +2 -2
- package/src/client/components/core/LogIn.js +2 -2
- package/src/client/components/core/LogOut.js +2 -2
- package/src/client/components/core/Modal.js +0 -1
- package/src/client/components/core/Panel.js +0 -1
- package/src/client/components/core/PanelForm.js +19 -19
- package/src/client/components/core/RichText.js +1 -2
- package/src/client/components/core/SocketIo.js +82 -29
- package/src/client/components/core/SocketIoHandler.js +75 -0
- package/src/client/components/core/Stream.js +143 -95
- package/src/client/components/core/Webhook.js +40 -7
- package/src/client/components/default/AppStoreDefault.js +5 -0
- package/src/client/components/default/LogInDefault.js +3 -3
- package/src/client/components/default/LogOutDefault.js +2 -2
- package/src/client/components/default/MenuDefault.js +5 -5
- package/src/client/components/default/SocketIoDefault.js +3 -51
- package/src/client/services/core/core.service.js +20 -8
- package/src/client/services/user/user.management.js +2 -2
- package/src/client/ssr/body/404.js +15 -11
- package/src/client/ssr/body/500.js +15 -11
- package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
- package/src/client/ssr/offline/NoNetworkConnection.js +11 -10
- package/src/client/ssr/pages/Test.js +11 -10
- package/src/index.js +24 -1
- package/src/runtime/express/Express.js +26 -9
- package/src/runtime/lampp/Dockerfile +9 -2
- package/src/runtime/lampp/Lampp.js +4 -3
- package/src/runtime/wp/Dockerfile +64 -0
- package/src/runtime/wp/Wp.js +497 -0
- package/src/server/auth.js +30 -6
- package/src/server/backup.js +19 -1
- package/src/server/client-build-docs.js +51 -110
- package/src/server/client-build.js +55 -64
- package/src/server/client-formatted.js +109 -57
- package/src/server/conf.js +19 -15
- package/src/server/ipfs-client.js +24 -1
- package/src/server/peer.js +8 -0
- package/src/server/runtime.js +25 -1
- package/src/server/start.js +21 -8
- package/src/ws/IoInterface.js +1 -10
- package/src/ws/IoServer.js +14 -33
- package/src/ws/core/channels/core.ws.chat.js +65 -20
- package/src/ws/core/channels/core.ws.mailer.js +113 -32
- package/src/ws/core/channels/core.ws.stream.js +90 -31
- package/src/ws/core/core.ws.connection.js +12 -33
- package/src/ws/core/core.ws.emit.js +10 -26
- package/src/ws/core/core.ws.server.js +25 -58
- package/src/ws/default/channels/default.ws.main.js +53 -12
- package/src/ws/default/default.ws.connection.js +26 -13
- package/src/ws/default/default.ws.server.js +30 -12
- package/src/client/components/default/ElementsDefault.js +0 -38
- package/src/ws/core/management/core.ws.chat.js +0 -8
- package/src/ws/core/management/core.ws.mailer.js +0 -16
- package/src/ws/core/management/core.ws.stream.js +0 -8
- package/src/ws/default/management/default.ws.main.js +0 -8
|
@@ -1,87 +1,139 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Module for formatting client-side code
|
|
2
|
+
* Module for formatting client-side code using esbuild for import rewriting and minification.
|
|
3
3
|
* @module src/server/client-formatted.js
|
|
4
4
|
* @namespace clientFormatted
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
+
import * as esbuild from 'esbuild';
|
|
10
|
+
import fs from 'fs-extra';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Escapes a string for safe use inside a RegExp.
|
|
15
|
+
* @param {string} s - The string to escape.
|
|
16
|
+
* @returns {string} The escaped string.
|
|
17
|
+
* @memberof clientFormatted
|
|
18
|
+
*/
|
|
19
|
+
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
20
|
+
|
|
9
21
|
/**
|
|
10
|
-
* Formats a source code string by removing 'html`' and 'css`'
|
|
22
|
+
* Formats a source code string by removing 'html`' and 'css`' tagged template prefixes.
|
|
23
|
+
* Used for SSR VM execution where the full esbuild pipeline is not needed.
|
|
11
24
|
* @param {string} src - The source code string.
|
|
12
25
|
* @returns {string} The formatted source code.
|
|
13
26
|
* @memberof clientFormatted
|
|
14
27
|
*/
|
|
15
|
-
const srcFormatted = (src) =>
|
|
16
|
-
src
|
|
17
|
-
.replaceAll(' html`', '`')
|
|
18
|
-
.replaceAll(' css`', '`')
|
|
19
|
-
.replaceAll('{html`', '{`')
|
|
20
|
-
.replaceAll('{css`', '{`')
|
|
21
|
-
.replaceAll('(html`', '(`')
|
|
22
|
-
.replaceAll('(css`', '(`')
|
|
23
|
-
.replaceAll('[html`', '[`')
|
|
24
|
-
.replaceAll('[css`', '[`');
|
|
28
|
+
const srcFormatted = (src) => src.replace(/(?<=[\s({[,;=+!?:^])(html|css)`/g, '`');
|
|
25
29
|
|
|
26
30
|
/**
|
|
27
31
|
* Converts a JavaScript object into a string that can be embedded in client-side code
|
|
28
32
|
* and parsed back into an object (e.g., 'JSON.parse(`{...}`)').
|
|
33
|
+
* Escapes backticks and template expression markers for safe template literal embedding.
|
|
29
34
|
* @param {*} data - The data to be stringified.
|
|
30
35
|
* @returns {string} A string representing the code to parse the JSON data.
|
|
31
36
|
* @memberof clientFormatted
|
|
32
37
|
*/
|
|
33
|
-
const JSONweb = (data) =>
|
|
38
|
+
const JSONweb = (data) => {
|
|
39
|
+
const json = JSON.stringify(data).replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
|
|
40
|
+
return 'JSON.parse(`' + json + '`)';
|
|
41
|
+
};
|
|
34
42
|
|
|
35
43
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* @param {
|
|
39
|
-
* @param {Array<object>} dists -
|
|
40
|
-
* @param {string} proxyPath - The proxy path for the application.
|
|
41
|
-
* @param {string} [
|
|
42
|
-
* @param {string} [
|
|
43
|
-
* @
|
|
44
|
+
* Creates an esbuild plugin that rewrites import paths for browser consumption.
|
|
45
|
+
* Handles dist library imports, relative imports, and marks all remaining imports as external.
|
|
46
|
+
* @param {object} options
|
|
47
|
+
* @param {Array<object>} [options.dists=[]] - Distribution objects with import_name and import_name_build.
|
|
48
|
+
* @param {string} options.proxyPath - The proxy path for the application.
|
|
49
|
+
* @param {string} [options.basePath=''] - The base path for the module type (e.g., 'components', 'services').
|
|
50
|
+
* @param {string} [options.module=''] - The module/component name for relative import resolution.
|
|
51
|
+
* @param {string} [options.baseHost=''] - The base host URL.
|
|
52
|
+
* @returns {import('esbuild').Plugin}
|
|
44
53
|
* @memberof clientFormatted
|
|
45
54
|
*/
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}${
|
|
66
|
-
|
|
67
|
-
|
|
55
|
+
const importRewritePlugin = ({ dists = [], proxyPath, basePath = '', module = '', baseHost = '' }) => ({
|
|
56
|
+
name: 'import-rewrite',
|
|
57
|
+
setup(build) {
|
|
58
|
+
const prefix = `${baseHost}${proxyPath !== '/' ? `${proxyPath}/` : '/'}`;
|
|
59
|
+
|
|
60
|
+
// Rewrite dist library imports (e.g., '@neodrag/vanilla' → '/proxyPath/dist/@neodrag-vanilla/index.js')
|
|
61
|
+
if (dists) {
|
|
62
|
+
for (const dist of dists) {
|
|
63
|
+
if (!dist.import_name) continue;
|
|
64
|
+
const filter = new RegExp(`^${escapeRegExp(dist.import_name)}$`);
|
|
65
|
+
build.onResolve({ filter }, () => ({
|
|
66
|
+
path: `${baseHost}${proxyPath !== '/' ? proxyPath : ''}${dist.import_name_build}`,
|
|
67
|
+
external: true,
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Rewrite relative imports to absolute paths based on proxy path and module
|
|
73
|
+
build.onResolve({ filter: /^\.\.?\// }, (args) => {
|
|
74
|
+
const basePrefix = `${prefix}${basePath ? `${basePath}/` : ''}`;
|
|
75
|
+
if (args.path.startsWith('./')) {
|
|
76
|
+
return {
|
|
77
|
+
path: `${basePrefix}${module ? `${module}/` : ''}${args.path.slice(2)}`,
|
|
78
|
+
external: true,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (args.path.startsWith('../')) {
|
|
82
|
+
return {
|
|
83
|
+
path: `${basePrefix}${args.path.slice(3)}`,
|
|
84
|
+
external: true,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Mark any remaining imports as external
|
|
90
|
+
build.onResolve({ filter: /.*/ }, (args) => {
|
|
91
|
+
if (args.kind === 'entry-point') return;
|
|
92
|
+
return { path: args.path, external: true };
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
});
|
|
68
96
|
|
|
69
97
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* @param {string}
|
|
74
|
-
* @param {
|
|
75
|
-
* @
|
|
98
|
+
* Transforms a JavaScript source file using esbuild with import path rewriting,
|
|
99
|
+
* tagged template stripping, and optional minification.
|
|
100
|
+
* Replaces the previous srcFormatted + componentFormatted/viewFormatted + UglifyJS pipeline.
|
|
101
|
+
* @param {string} srcPath - Path to the source file.
|
|
102
|
+
* @param {object} options
|
|
103
|
+
* @param {Array<object>} [options.dists=[]] - Distribution objects with import names.
|
|
104
|
+
* @param {string} options.proxyPath - The proxy path for the application.
|
|
105
|
+
* @param {string} [options.basePath=''] - Base path for the module type (e.g., 'components', 'services').
|
|
106
|
+
* @param {string} [options.module=''] - Module name for relative import resolution.
|
|
107
|
+
* @param {string} [options.baseHost=''] - Base host URL.
|
|
108
|
+
* @param {boolean} [options.minify=false] - Whether to minify the output.
|
|
109
|
+
* @returns {Promise<string>} The transformed source code.
|
|
76
110
|
* @memberof clientFormatted
|
|
77
111
|
*/
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
83
|
-
const
|
|
84
|
-
|
|
112
|
+
const transformClientJs = async (
|
|
113
|
+
srcPath,
|
|
114
|
+
{ dists = [], proxyPath, basePath = '', module = '', baseHost = '', minify: shouldMinify = false } = {},
|
|
115
|
+
) => {
|
|
116
|
+
const src = fs.readFileSync(srcPath, 'utf8');
|
|
117
|
+
const stripped = srcFormatted(src);
|
|
118
|
+
|
|
119
|
+
const result = await esbuild.build({
|
|
120
|
+
stdin: {
|
|
121
|
+
contents: stripped,
|
|
122
|
+
loader: 'js',
|
|
123
|
+
resolveDir: path.dirname(path.resolve(srcPath)),
|
|
124
|
+
sourcefile: srcPath,
|
|
125
|
+
},
|
|
126
|
+
bundle: true,
|
|
127
|
+
write: false,
|
|
128
|
+
format: 'esm',
|
|
129
|
+
platform: 'browser',
|
|
130
|
+
target: 'esnext',
|
|
131
|
+
minify: shouldMinify,
|
|
132
|
+
logLevel: 'warning',
|
|
133
|
+
plugins: [importRewritePlugin({ dists, proxyPath, basePath, module, baseHost })],
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
return result.outputFiles[0].text;
|
|
85
137
|
};
|
|
86
138
|
|
|
87
|
-
export { srcFormatted, JSONweb,
|
|
139
|
+
export { srcFormatted, JSONweb, transformClientJs };
|
package/src/server/conf.js
CHANGED
|
@@ -1275,18 +1275,6 @@ const awaitDeployMonitor = async (init = false, deltaMs = 1000) => {
|
|
|
1275
1275
|
if (Underpost.env.get('await-deploy')) return await awaitDeployMonitor();
|
|
1276
1276
|
};
|
|
1277
1277
|
|
|
1278
|
-
/**
|
|
1279
|
-
* @method getCronBackUpFolder
|
|
1280
|
-
* @description Gets the cron back up folder.
|
|
1281
|
-
* @param {string} host - The host.
|
|
1282
|
-
* @param {string} path - The path.
|
|
1283
|
-
* @returns {string} - The cron back up folder.
|
|
1284
|
-
* @memberof ServerConfBuilder
|
|
1285
|
-
*/
|
|
1286
|
-
const getCronBackUpFolder = (host = '', path = '') => {
|
|
1287
|
-
return `${host}${path.replace(/\\/g, '/').replace(`/`, '-')}`;
|
|
1288
|
-
};
|
|
1289
|
-
|
|
1290
1278
|
/**
|
|
1291
1279
|
* @method mergeFile
|
|
1292
1280
|
* @description Merges the file.
|
|
@@ -1499,8 +1487,25 @@ const buildCliDoc = (program, oldVersion, newVersion) => {
|
|
|
1499
1487
|
md = md.replaceAll(oldVersion, newVersion);
|
|
1500
1488
|
fs.writeFileSync(`./src/client/public/nexodev/docs/references/Command Line Interface.md`, md, 'utf8');
|
|
1501
1489
|
fs.writeFileSync(`./CLI-HELP.md`, md, 'utf8');
|
|
1502
|
-
|
|
1503
|
-
|
|
1490
|
+
|
|
1491
|
+
// Update README.md: replace version and CLI index section between comment tags
|
|
1492
|
+
let readme = fs.readFileSync(`./README.md`, 'utf8');
|
|
1493
|
+
readme = readme.replaceAll(oldVersion, newVersion);
|
|
1494
|
+
const cliStartTag = '<!-- cli-index-start -->';
|
|
1495
|
+
const cliEndTag = '<!-- cli-index-end -->';
|
|
1496
|
+
const startIdx = readme.indexOf(cliStartTag);
|
|
1497
|
+
const endIdx = readme.indexOf(cliEndTag);
|
|
1498
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
1499
|
+
readme =
|
|
1500
|
+
readme.substring(0, startIdx) +
|
|
1501
|
+
cliStartTag +
|
|
1502
|
+
'\n' +
|
|
1503
|
+
baseOptions +
|
|
1504
|
+
'\n' +
|
|
1505
|
+
cliEndTag +
|
|
1506
|
+
readme.substring(endIdx + cliEndTag.length);
|
|
1507
|
+
}
|
|
1508
|
+
fs.writeFileSync('./README.md', readme, 'utf8');
|
|
1504
1509
|
};
|
|
1505
1510
|
|
|
1506
1511
|
/**
|
|
@@ -1743,7 +1748,6 @@ export {
|
|
|
1743
1748
|
getDataDeploy,
|
|
1744
1749
|
validateTemplatePath,
|
|
1745
1750
|
buildReplicaId,
|
|
1746
|
-
getCronBackUpFolder,
|
|
1747
1751
|
mergeFile,
|
|
1748
1752
|
getPathsSSR,
|
|
1749
1753
|
buildKindPorts,
|
|
@@ -244,7 +244,11 @@ const unpinCid = async (cid) => {
|
|
|
244
244
|
});
|
|
245
245
|
if (!clusterRes.ok) {
|
|
246
246
|
const text = await clusterRes.text();
|
|
247
|
-
|
|
247
|
+
if (clusterRes.status === 404) {
|
|
248
|
+
logger.info(`IPFS Cluster unpin – CID already not pinned: ${cid}`);
|
|
249
|
+
} else {
|
|
250
|
+
logger.warn(`IPFS Cluster unpin failed (${clusterRes.status}): ${text}`);
|
|
251
|
+
}
|
|
248
252
|
} else {
|
|
249
253
|
logger.info(`IPFS Cluster unpin OK – CID: ${cid}`);
|
|
250
254
|
}
|
|
@@ -428,6 +432,25 @@ const IpfsClient = {
|
|
|
428
432
|
listClusterPins,
|
|
429
433
|
listKuboPins,
|
|
430
434
|
removeMfsPath,
|
|
435
|
+
/**
|
|
436
|
+
* Check whether a single CID is currently pinned on the local Kubo node.
|
|
437
|
+
* Uses the pin/ls?arg=<cid> endpoint which returns only that one pin
|
|
438
|
+
* (much cheaper than fetching the full list).
|
|
439
|
+
*
|
|
440
|
+
* @param {string} cid - IPFS Content Identifier to check.
|
|
441
|
+
* @returns {Promise<boolean>} true when the CID is pinned.
|
|
442
|
+
*/
|
|
443
|
+
isCidPinned: async (cid) => {
|
|
444
|
+
const kuboUrl = getIpfsApiUrl();
|
|
445
|
+
try {
|
|
446
|
+
const res = await fetch(`${kuboUrl}/api/v0/pin/ls?arg=${encodeURIComponent(cid)}&type=all`, { method: 'POST' });
|
|
447
|
+
if (!res.ok) return false;
|
|
448
|
+
const json = await res.json();
|
|
449
|
+
return !!(json.Keys && json.Keys[cid]);
|
|
450
|
+
} catch {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
},
|
|
431
454
|
};
|
|
432
455
|
|
|
433
456
|
export { IpfsClient };
|
package/src/server/peer.js
CHANGED
|
@@ -44,6 +44,14 @@ const logger = loggerFactory(import.meta);
|
|
|
44
44
|
*/
|
|
45
45
|
const createPeerServer = async ({ port, origins, path }) => {
|
|
46
46
|
logger.info('origins', origins);
|
|
47
|
+
|
|
48
|
+
// In development, allow the local client origin (peer runs on port+1 relative to the client)
|
|
49
|
+
if (process.env.NODE_ENV === 'development') {
|
|
50
|
+
const clientPort = port - 1;
|
|
51
|
+
const devOrigin = `http://localhost:${clientPort}`;
|
|
52
|
+
if (!origins.includes(devOrigin)) origins.push(devOrigin);
|
|
53
|
+
}
|
|
54
|
+
|
|
47
55
|
/** @type {import('peer').IConfig} */
|
|
48
56
|
const options = {
|
|
49
57
|
port,
|
package/src/server/runtime.js
CHANGED
|
@@ -11,6 +11,7 @@ import * as promClient from 'prom-client';
|
|
|
11
11
|
import { loggerFactory } from './logger.js';
|
|
12
12
|
import { newInstance } from '../client/components/core/CommonJs.js';
|
|
13
13
|
import { Lampp } from '../runtime/lampp/Lampp.js';
|
|
14
|
+
import { WpService } from '../runtime/wp/Wp.js';
|
|
14
15
|
import { getInstanceContext, readConfJson } from './conf.js';
|
|
15
16
|
|
|
16
17
|
import ExpressService from '../runtime/express/Express.js';
|
|
@@ -71,6 +72,9 @@ const buildRuntime = async () => {
|
|
|
71
72
|
valkey,
|
|
72
73
|
apiBaseHost,
|
|
73
74
|
useLocalSsl,
|
|
75
|
+
grpc,
|
|
76
|
+
repository,
|
|
77
|
+
wp,
|
|
74
78
|
} = confServer[host][path];
|
|
75
79
|
|
|
76
80
|
// Calculate context data
|
|
@@ -116,6 +120,7 @@ const buildRuntime = async () => {
|
|
|
116
120
|
peer,
|
|
117
121
|
valkey,
|
|
118
122
|
apiBaseHost,
|
|
123
|
+
grpc,
|
|
119
124
|
redirectTarget,
|
|
120
125
|
rootHostPath,
|
|
121
126
|
confSSR,
|
|
@@ -129,7 +134,7 @@ const buildRuntime = async () => {
|
|
|
129
134
|
|
|
130
135
|
case 'lampp':
|
|
131
136
|
{
|
|
132
|
-
const { disabled } =
|
|
137
|
+
const { disabled } = Lampp.createApp({
|
|
133
138
|
port,
|
|
134
139
|
host,
|
|
135
140
|
path,
|
|
@@ -143,6 +148,25 @@ const buildRuntime = async () => {
|
|
|
143
148
|
await Underpost.start.listenPortController(Underpost.start.listenServerFactory(), port, runningData);
|
|
144
149
|
}
|
|
145
150
|
break;
|
|
151
|
+
|
|
152
|
+
case 'wp':
|
|
153
|
+
{
|
|
154
|
+
const { disabled } = WpService.createApp({
|
|
155
|
+
port,
|
|
156
|
+
host,
|
|
157
|
+
pathRoute: path,
|
|
158
|
+
repository,
|
|
159
|
+
db,
|
|
160
|
+
wp,
|
|
161
|
+
redirect,
|
|
162
|
+
redirectTarget,
|
|
163
|
+
resetRouter: currentPort === initPort,
|
|
164
|
+
});
|
|
165
|
+
if (disabled) continue;
|
|
166
|
+
await Underpost.start.listenPortController(Underpost.start.listenServerFactory(), port, runningData);
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
|
|
146
170
|
default:
|
|
147
171
|
break;
|
|
148
172
|
}
|
package/src/server/start.js
CHANGED
|
@@ -9,6 +9,7 @@ import { awaitDeployMonitor } from './conf.js';
|
|
|
9
9
|
import { actionInitLog, loggerFactory } from './logger.js';
|
|
10
10
|
import { shellCd, shellExec } from './process.js';
|
|
11
11
|
import Underpost from '../index.js';
|
|
12
|
+
import isInsideContainer from 'is-inside-container';
|
|
12
13
|
const logger = loggerFactory(import.meta);
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -147,18 +148,25 @@ class UnderpostStartUp {
|
|
|
147
148
|
* @param {string} deployId - The ID of the deployment.
|
|
148
149
|
* @param {string} env - The environment of the deployment.
|
|
149
150
|
* @param {Object} options - Options for the build.
|
|
151
|
+
* @param {boolean} options.skipPullBase - Whether to skip pulling the base code and use the current workspace code directly.
|
|
150
152
|
* @param {boolean} options.underpostQuicklyInstall - Whether to use underpost quickly install.
|
|
151
153
|
* @memberof UnderpostStartUp
|
|
152
154
|
*/
|
|
153
|
-
async build(
|
|
155
|
+
async build(
|
|
156
|
+
deployId = 'dd-default',
|
|
157
|
+
env = 'development',
|
|
158
|
+
options = { underpostQuicklyInstall: false, skipPullBase: false },
|
|
159
|
+
) {
|
|
154
160
|
const buildBasePath = `/home/dd`;
|
|
155
161
|
const repoName = `engine-${deployId.split('-')[1]}`;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
+
if (!options.skipPullBase) {
|
|
163
|
+
shellExec(`cd ${buildBasePath} && underpost clone ${process.env.GITHUB_USERNAME}/${repoName}`);
|
|
164
|
+
shellExec(`mkdir -p ${buildBasePath}/engine`);
|
|
165
|
+
shellExec(`cd ${buildBasePath} && sudo cp -a ./${repoName}/. ./engine`);
|
|
166
|
+
shellExec(`cd ${buildBasePath} && sudo rm -rf ./${repoName}`);
|
|
167
|
+
shellExec(`cd ${buildBasePath}/engine && underpost clone ${process.env.GITHUB_USERNAME}/${repoName}-private`);
|
|
168
|
+
shellExec(`cd ${buildBasePath}/engine && sudo mv ./${repoName}-private ./engine-private`);
|
|
169
|
+
}
|
|
162
170
|
shellCd(`${buildBasePath}/engine`);
|
|
163
171
|
shellExec(options?.underpostQuicklyInstall ? `underpost install` : `npm install`);
|
|
164
172
|
shellExec(`node bin env ${deployId} ${env}`);
|
|
@@ -167,7 +175,7 @@ class UnderpostStartUp {
|
|
|
167
175
|
for (const itcScript of itcScripts)
|
|
168
176
|
if (itcScript.match(deployId)) shellExec(`node ./engine-private/itc-scripts/${itcScript}`);
|
|
169
177
|
}
|
|
170
|
-
|
|
178
|
+
shellExec(`node bin client ${deployId}`);
|
|
171
179
|
},
|
|
172
180
|
/**
|
|
173
181
|
* Runs a deployment.
|
|
@@ -191,6 +199,11 @@ class UnderpostStartUp {
|
|
|
191
199
|
shellExec(`npm ${runCmd} ${deployId}`, { async: true });
|
|
192
200
|
await awaitDeployMonitor(true);
|
|
193
201
|
Underpost.env.set('container-status', `${deployId}-${env}-running-deployment`);
|
|
202
|
+
if (env === 'production' && isInsideContainer()) {
|
|
203
|
+
Underpost.env.clean();
|
|
204
|
+
shellExec(`sudo rm -rf /home/dd/engine/engine-private`);
|
|
205
|
+
if (fs.existsSync('/etc/config/.env.production')) fs.removeSync('/etc/config/.env.production');
|
|
206
|
+
}
|
|
194
207
|
},
|
|
195
208
|
};
|
|
196
209
|
}
|
package/src/ws/IoInterface.js
CHANGED
|
@@ -126,13 +126,4 @@ class IoChannel {
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
* Backward compatibility function to create a new channel instance.
|
|
131
|
-
* @memberof SocketIoInterface
|
|
132
|
-
* @function IoCreateChannel
|
|
133
|
-
* @param {ChannelInterface} IoInterface - The interface object defining the channel's behavior.
|
|
134
|
-
* @returns {IoChannel} An instance of the IoChannel class.
|
|
135
|
-
*/
|
|
136
|
-
const IoCreateChannel = (IoInterface) => new IoChannel(IoInterface);
|
|
137
|
-
|
|
138
|
-
export { IoChannel, IoCreateChannel };
|
|
129
|
+
export { IoChannel };
|
package/src/ws/IoServer.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Module for creating and managing WebSocket servers.
|
|
3
|
-
* @module
|
|
4
|
-
* @namespace SocketIoServer
|
|
3
|
+
* @module ws/IoServer
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
'use strict';
|
|
@@ -9,33 +8,27 @@
|
|
|
9
8
|
import { Server } from 'socket.io';
|
|
10
9
|
import { loggerFactory } from '../server/logger.js';
|
|
11
10
|
import Underpost from '../index.js';
|
|
12
|
-
import http from 'http';
|
|
13
11
|
|
|
14
12
|
const logger = loggerFactory(import.meta);
|
|
15
13
|
|
|
16
14
|
/**
|
|
17
|
-
* @class
|
|
18
|
-
* @
|
|
19
|
-
*
|
|
20
|
-
* @classdesc Provides a static factory method to create and configure a Socket.IO server,
|
|
21
|
-
* encapsulating WebSocket server initialization logic and CORS configuration.
|
|
15
|
+
* @class IoServer
|
|
16
|
+
* @classdesc Factory for creating and configuring Socket.IO server instances
|
|
17
|
+
* with CORS configuration and HTTP server attachment.
|
|
22
18
|
*/
|
|
23
|
-
class
|
|
19
|
+
class IoServer {
|
|
24
20
|
/**
|
|
25
21
|
* Creates a new WebSocket server instance attached to an HTTP server.
|
|
26
22
|
*
|
|
27
23
|
* @static
|
|
28
|
-
* @param {http.Server} httpServer - The HTTP server instance to attach
|
|
29
|
-
* @param {Object} options - Configuration options
|
|
30
|
-
* @param {string[]} options.origins -
|
|
31
|
-
* @param {string} options.path -
|
|
32
|
-
* @param {function(import('socket.io').Socket): void}
|
|
33
|
-
* @returns {
|
|
34
|
-
* @returns {import('socket.io').ServerOptions} return.options - The final options object used to create the WebSocket server.
|
|
35
|
-
* @returns {import('socket.io').Server} return.ioServer - The created and listening WebSocket server instance.
|
|
36
|
-
* @returns {object} return.meta - The module's import meta object (`import.meta`).
|
|
24
|
+
* @param {import('http').Server} httpServer - The HTTP server instance to attach to.
|
|
25
|
+
* @param {Object} options - Configuration options.
|
|
26
|
+
* @param {string[]} options.origins - Allowed CORS origins.
|
|
27
|
+
* @param {string} options.path - Base API path. Socket.IO path is appended automatically.
|
|
28
|
+
* @param {function(import('socket.io').Socket): void} connectionHandler - Handler for new connections.
|
|
29
|
+
* @returns {{ options: import('socket.io').ServerOptions, ioServer: import('socket.io').Server, meta: ImportMeta }}
|
|
37
30
|
*/
|
|
38
|
-
static create(httpServer, options = {},
|
|
31
|
+
static create(httpServer, options = {}, connectionHandler = () => {}) {
|
|
39
32
|
logger.info('origins', options.origins);
|
|
40
33
|
const wsOptions = {
|
|
41
34
|
cors: {
|
|
@@ -55,12 +48,11 @@ class IoServerClass {
|
|
|
55
48
|
],
|
|
56
49
|
credentials: true,
|
|
57
50
|
},
|
|
58
|
-
// Ensure the path ends correctly, appending '/socket.io/'
|
|
59
51
|
path: options.path !== '/' ? `${options.path}/socket.io/` : '/socket.io/',
|
|
60
52
|
};
|
|
61
53
|
|
|
62
54
|
const ioServerInstance = Underpost.start.listenServerFactory(() =>
|
|
63
|
-
new Server(httpServer, wsOptions).on('connection',
|
|
55
|
+
new Server(httpServer, wsOptions).on('connection', connectionHandler),
|
|
64
56
|
);
|
|
65
57
|
|
|
66
58
|
logger.info('Socket.IO Server created and listening', { path: wsOptions.path });
|
|
@@ -73,15 +65,4 @@ class IoServerClass {
|
|
|
73
65
|
}
|
|
74
66
|
}
|
|
75
67
|
|
|
76
|
-
|
|
77
|
-
* Backward compatibility export for the server creation function.
|
|
78
|
-
* @memberof SocketIoServer
|
|
79
|
-
* @function IoServer
|
|
80
|
-
* @param {http.Server} httpServer - The HTTP server instance.
|
|
81
|
-
* @param {Object} options - Configuration options.
|
|
82
|
-
* @param {function(import('socket.io').Socket): void} ConnectionHandler - The connection handler function.
|
|
83
|
-
* @returns {Object} The server configuration object.
|
|
84
|
-
*/
|
|
85
|
-
const IoServer = IoServerClass.create;
|
|
86
|
-
|
|
87
|
-
export { IoServerClass, IoServer };
|
|
68
|
+
export { IoServer };
|
|
@@ -1,23 +1,68 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Chat WebSocket channel — broadcasts messages to all connected sockets except the sender.
|
|
3
|
+
* @module ws/core/channels/core.ws.chat
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { IoChannel } from '../../IoInterface.js';
|
|
7
|
+
import { CoreWsEmitter } from '../core.ws.emit.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @class CoreWsChatChannel
|
|
11
|
+
* @classdesc Manages the chat WebSocket channel with per-instance state.
|
|
12
|
+
* Broadcasts incoming messages to all other connected sockets.
|
|
13
|
+
*/
|
|
14
|
+
class CoreWsChatChannel {
|
|
15
|
+
/** @type {Object.<string, Object>} Per-instance state keyed by wsManagementId. */
|
|
16
|
+
static #state = {};
|
|
17
|
+
|
|
18
|
+
/** @type {IoChannel} */
|
|
19
|
+
static #io = new IoChannel({
|
|
20
|
+
channel: 'chat',
|
|
21
|
+
controller(socket, client, payload, wsManagementId) {
|
|
22
|
+
for (const socketId of Object.keys(client)) {
|
|
23
|
+
if (socketId !== socket.id) {
|
|
24
|
+
CoreWsEmitter.emit('chat', client[socketId], { id: socket.id, ...payload });
|
|
25
|
+
}
|
|
14
26
|
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/** @returns {Object.<string, import('socket.io').Socket>} Connected sockets map. */
|
|
31
|
+
static get client() {
|
|
32
|
+
return this.#io.client;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** @returns {string} Channel name. */
|
|
36
|
+
static get channel() {
|
|
37
|
+
return this.#io.channel;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Initializes state for a server instance.
|
|
42
|
+
* @param {string} wsManagementId - Unique server context ID.
|
|
43
|
+
*/
|
|
44
|
+
static init(wsManagementId) {
|
|
45
|
+
this.#state[wsManagementId] = {};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Registers a socket connection.
|
|
50
|
+
* @param {import('socket.io').Socket} socket
|
|
51
|
+
* @param {string} wsManagementId
|
|
52
|
+
*/
|
|
53
|
+
static connection(socket, wsManagementId) {
|
|
54
|
+
return this.#io.connection(socket, wsManagementId);
|
|
55
|
+
}
|
|
20
56
|
|
|
21
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Handles socket disconnection.
|
|
59
|
+
* @param {import('socket.io').Socket} socket
|
|
60
|
+
* @param {string} reason
|
|
61
|
+
* @param {string} wsManagementId
|
|
62
|
+
*/
|
|
63
|
+
static disconnect(socket, reason, wsManagementId) {
|
|
64
|
+
return this.#io.disconnect(socket, reason, wsManagementId);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
22
67
|
|
|
23
|
-
export { CoreWsChatChannel
|
|
68
|
+
export { CoreWsChatChannel };
|