underpost 2.85.0 → 2.85.7
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.development +2 -1
- package/.env.production +2 -1
- package/.env.test +2 -1
- package/.github/workflows/release.cd.yml +4 -13
- package/.vscode/zed.keymap.json +22 -0
- package/Dockerfile +3 -1
- package/README.md +3 -3
- package/bin/deploy.js +4 -2
- package/bin/file.js +4 -0
- package/bin/vs.js +4 -4
- package/bin/zed.js +5 -2
- package/cli.md +15 -11
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/manifests/deployment/mongo-express/deployment.yaml +2 -0
- package/package.json +2 -2
- package/src/cli/baremetal.js +3 -3
- package/src/cli/deploy.js +138 -21
- package/src/cli/image.js +3 -2
- package/src/cli/index.js +7 -3
- package/src/cli/repository.js +38 -46
- package/src/cli/run.js +229 -19
- package/src/client/components/core/CommonJs.js +1 -0
- package/src/client/components/core/SocketIo.js +5 -1
- package/src/client/sw/default.sw.js +193 -97
- package/src/client.dev.js +1 -1
- package/src/index.js +1 -1
- package/src/proxy.js +1 -1
- package/src/runtime/express/Express.js +4 -1
- package/src/server/auth.js +2 -1
- package/src/server/conf.js +132 -15
- package/src/server/proxy.js +53 -26
- package/src/server/start.js +13 -3
- package/src/server/tls.js +1 -1
|
@@ -1,108 +1,204 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
(async () => {
|
|
32
|
-
// Enable navigation preload if it's supported.
|
|
33
|
-
// See https://developers.google.com/web/updates/2017/02/navigation-preload
|
|
34
|
-
if ('navigationPreload' in self.registration) {
|
|
35
|
-
await self.registration.navigationPreload.enable();
|
|
36
|
-
}
|
|
37
|
-
})(),
|
|
38
|
-
);
|
|
39
|
-
// Tell the active service worker to take control of the page immediately.
|
|
40
|
-
self.clients.claim();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
self.addEventListener('fetch', (event) => {
|
|
44
|
-
// Cache-First Strategy
|
|
45
|
-
event.respondWith(
|
|
46
|
-
(async () => {
|
|
47
|
-
// First, try to use the navigation preload response if it's supported.
|
|
48
|
-
try {
|
|
49
|
-
const preloadResponse = await event.preloadResponse;
|
|
50
|
-
if (preloadResponse) return preloadResponse;
|
|
51
|
-
return await fetch(event.request);
|
|
52
|
-
} catch (error) {
|
|
53
|
-
console.error('Fetch failed; returning offline page instead.', event.request.url, error);
|
|
54
|
-
// Fallback to the offline page.
|
|
55
|
-
const path = PRE_CACHED_RESOURCES.find((path) => event.request.url.match(path.replaceAll('/index.html', '')));
|
|
1
|
+
/**
|
|
2
|
+
* This module provides a configurable Progressive Web App (PWA) service worker and caching strategies.
|
|
3
|
+
* It supports precaching assets, runtime caching with stale-while-revalidate strategy,
|
|
4
|
+
* and offline fallback handling.
|
|
5
|
+
* @module src/client/sw/default.sw.js
|
|
6
|
+
* @namespace PwaServiceWorker
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Class representing a Progressive Web App (PWA) Service Worker with caching strategies.
|
|
11
|
+
* @class
|
|
12
|
+
* @memberof PwaServiceWorker
|
|
13
|
+
*/
|
|
14
|
+
class PwaServiceWorker {
|
|
15
|
+
/**
|
|
16
|
+
* Initializes the service worker configuration by reading from self.renderPayload.
|
|
17
|
+
* If properties are not found, defaults are used.
|
|
18
|
+
* @constructor
|
|
19
|
+
* @property {Array<string>} PRE_CACHED_RESOURCES - List of resources to precache.
|
|
20
|
+
* @property {string} CACHE_NAME - Name of the cache storage.
|
|
21
|
+
* @property {string} PROXY_PATH - Base path for proxying requests.
|
|
22
|
+
*/
|
|
23
|
+
constructor() {
|
|
24
|
+
// Configuration properties equivalent to the original global constants
|
|
25
|
+
this.PRE_CACHED_RESOURCES = self.renderPayload?.PRE_CACHED_RESOURCES ?? [];
|
|
26
|
+
this.CACHE_NAME = self.renderPayload?.CACHE_NAME ?? 'app-cache';
|
|
27
|
+
this.PROXY_PATH = self.renderPayload?.PROXY_PATH ?? '/';
|
|
28
|
+
|
|
29
|
+
console.log(`Service Worker Initialized. Cache: ${this.CACHE_NAME}, Proxy: ${this.PROXY_PATH}`);
|
|
30
|
+
}
|
|
56
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Registers event listeners for the service worker lifecycle and requests.
|
|
34
|
+
* @method
|
|
35
|
+
* @memberof PwaServiceWorker
|
|
36
|
+
*/
|
|
37
|
+
run() {
|
|
38
|
+
// Bind methods to 'this' (the instance) before attaching to self
|
|
39
|
+
self.addEventListener('install', this._onInstall.bind(this));
|
|
40
|
+
self.addEventListener('activate', this._onActivate.bind(this));
|
|
41
|
+
self.addEventListener('fetch', this._onFetch.bind(this));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Handles the 'install' event. Skips waiting and precaches static assets.
|
|
46
|
+
* @param {ExtendableEvent} event
|
|
47
|
+
* @memberof PwaServiceWorker
|
|
48
|
+
*/
|
|
49
|
+
_onInstall(event) {
|
|
50
|
+
// Activate right away
|
|
51
|
+
self.skipWaiting();
|
|
52
|
+
|
|
53
|
+
event.waitUntil(
|
|
54
|
+
(async () => {
|
|
55
|
+
// Open the app's cache using the configured name.
|
|
56
|
+
const cache = await caches.open(this.CACHE_NAME);
|
|
57
|
+
// Cache all static resources.
|
|
57
58
|
try {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const cache = await caches.open(CACHE_NAME);
|
|
61
|
-
const preCachedResponse = await cache.match(path);
|
|
62
|
-
if (!preCachedResponse) throw new Error(error.message);
|
|
63
|
-
return preCachedResponse;
|
|
59
|
+
console.log(`Precaching ${this.PRE_CACHED_RESOURCES.length} resources...`);
|
|
60
|
+
await cache.addAll(this.PRE_CACHED_RESOURCES);
|
|
64
61
|
} catch (error) {
|
|
65
|
-
console.error('Error
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
62
|
+
console.error('Error during precaching resources:', error);
|
|
63
|
+
}
|
|
64
|
+
})(),
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Handles the 'activate' event. Enables navigation preload and takes control
|
|
70
|
+
* of uncontrolled clients immediately.
|
|
71
|
+
* @param {ExtendableEvent} event
|
|
72
|
+
* @memberof PwaServiceWorker
|
|
73
|
+
*/
|
|
74
|
+
_onActivate(event) {
|
|
75
|
+
event.waitUntil(
|
|
76
|
+
(async () => {
|
|
77
|
+
// Enable navigation preload if it's supported.
|
|
78
|
+
if ('navigationPreload' in self.registration) {
|
|
79
|
+
await self.registration.navigationPreload.enable();
|
|
80
|
+
console.log('Navigation Preload enabled.');
|
|
81
|
+
}
|
|
82
|
+
})(),
|
|
83
|
+
);
|
|
84
|
+
// Tell the active service worker to take control of the page immediately.
|
|
85
|
+
self.clients.claim();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Handles the 'fetch' event, implementing the Cache-First strategy with
|
|
90
|
+
* complex offline and maintenance fallbacks.
|
|
91
|
+
* @param {FetchEvent} event
|
|
92
|
+
* @memberof PwaServiceWorker
|
|
93
|
+
*/
|
|
94
|
+
_onFetch(event) {
|
|
95
|
+
// Only handle HTTP/HTTPS requests that are not cross-origin (optional, but robust)
|
|
96
|
+
if (event.request.url.startsWith('http')) {
|
|
97
|
+
event.respondWith(this._handleFetchRequest(event));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Core logic to handle fetching, caching, and fallbacks.
|
|
103
|
+
* @param {FetchEvent} event
|
|
104
|
+
* @returns {Promise<Response>}
|
|
105
|
+
* @memberof PwaServiceWorker
|
|
106
|
+
*/
|
|
107
|
+
async _handleFetchRequest(event) {
|
|
108
|
+
// 1. Try Navigation Preload (if available) or network first
|
|
109
|
+
try {
|
|
110
|
+
const preloadResponse = await event.preloadResponse;
|
|
111
|
+
if (preloadResponse) return preloadResponse;
|
|
112
|
+
|
|
113
|
+
// Fall through to network request if no preload response
|
|
114
|
+
const networkResponse = await fetch(event.request);
|
|
115
|
+
|
|
116
|
+
// OPTIONAL: If the network request is successful, cache it for future use (stale-while-revalidate logic)
|
|
117
|
+
// Omitted for strict equivalence, as original only had complex fallback, not runtime caching.
|
|
118
|
+
|
|
119
|
+
return networkResponse;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error('Network request failed. Attempting cache/fallback logic.', event.request.url, error);
|
|
122
|
+
|
|
123
|
+
// 2. Try to match the request in the cache
|
|
124
|
+
try {
|
|
125
|
+
const cachedResponse = await caches.match(event.request);
|
|
126
|
+
if (cachedResponse) {
|
|
127
|
+
console.log(`Cache hit for: ${event.request.url}`);
|
|
128
|
+
return cachedResponse;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 3. Try to match a precached resource path (e.g., if requesting /page, match /page/index.html)
|
|
132
|
+
const path = this.PRE_CACHED_RESOURCES.find((p) => event.request.url.match(p.replaceAll('/index.html', '')));
|
|
133
|
+
|
|
134
|
+
if (path) {
|
|
135
|
+
const cache = await caches.open(this.CACHE_NAME);
|
|
136
|
+
const preCachedResponse = await cache.match(path);
|
|
137
|
+
if (preCachedResponse) {
|
|
138
|
+
console.log(`Matched precached resource for: ${event.request.url} via path: ${path}`);
|
|
139
|
+
return preCachedResponse;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// If neither cache match nor precache path match worked, fall through to complex fallback
|
|
144
|
+
throw new Error('Cache miss and no precache match.');
|
|
145
|
+
} catch (cacheError) {
|
|
146
|
+
console.error('Error in primary cache lookup. Falling back to offline/maintenance pages.', {
|
|
147
|
+
url: event.request.url,
|
|
148
|
+
cacheError,
|
|
149
|
+
onLine: navigator.onLine,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// 4. Complex Fallback Logic (Offline or Maintenance)
|
|
153
|
+
try {
|
|
154
|
+
const cache = await caches.open(this.CACHE_NAME);
|
|
155
|
+
|
|
156
|
+
if (!navigator.onLine) {
|
|
157
|
+
// A. OFFLINE FALLBACK
|
|
85
158
|
if (event.request.method.toUpperCase() === 'GET') {
|
|
86
|
-
const
|
|
87
|
-
const preCachedResponse = await cache.match(
|
|
88
|
-
|
|
89
|
-
);
|
|
90
|
-
|
|
159
|
+
const offlinePath = `${this.PROXY_PATH === '/' ? '' : this.PROXY_PATH}/offline/index.html`;
|
|
160
|
+
const preCachedResponse = await cache.match(offlinePath);
|
|
161
|
+
|
|
162
|
+
if (!preCachedResponse) throw new Error(`Offline page not found in cache: ${offlinePath}`);
|
|
163
|
+
|
|
164
|
+
console.log('Serving offline HTML page.');
|
|
91
165
|
return preCachedResponse;
|
|
92
166
|
}
|
|
93
|
-
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
} catch (error) {
|
|
98
|
-
console.error('Error opening cache for offline page', event.request.url, error);
|
|
99
|
-
const response = new Response(JSON.stringify({ status: 'error', message: error.message }));
|
|
100
|
-
// response.status = 200;
|
|
167
|
+
|
|
168
|
+
// B. OFFLINE API FALLBACK (Non-GET requests)
|
|
169
|
+
console.log('Serving offline JSON response for non-GET request.');
|
|
170
|
+
const response = new Response(JSON.stringify({ status: 'error', message: 'offline test response' }));
|
|
101
171
|
response.headers.set('Content-Type', 'application/json');
|
|
102
172
|
return response;
|
|
103
173
|
}
|
|
174
|
+
|
|
175
|
+
// C. MAINTENANCE FALLBACK (Online, but network failed - interpreted as maintenance)
|
|
176
|
+
if (event.request.method.toUpperCase() === 'GET') {
|
|
177
|
+
const maintenancePath = `${this.PROXY_PATH === '/' ? '' : this.PROXY_PATH}/maintenance/index.html`;
|
|
178
|
+
const preCachedResponse = await cache.match(maintenancePath);
|
|
179
|
+
|
|
180
|
+
if (!preCachedResponse) throw new Error(`Maintenance page not found in cache: ${maintenancePath}`);
|
|
181
|
+
|
|
182
|
+
console.log('Serving maintenance HTML page.');
|
|
183
|
+
return preCachedResponse;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// D. MAINTENANCE API FALLBACK (Non-GET requests)
|
|
187
|
+
console.log('Serving maintenance JSON response for non-GET request.');
|
|
188
|
+
const response = new Response(JSON.stringify({ status: 'error', message: 'server in maintenance' }));
|
|
189
|
+
response.headers.set('Content-Type', 'application/json');
|
|
190
|
+
return response;
|
|
191
|
+
} catch (finalError) {
|
|
192
|
+
// 5. Final fail-safe response
|
|
193
|
+
console.error('Final fail-safe execution failed.', event.request.url, finalError);
|
|
194
|
+
const response = new Response(JSON.stringify({ status: 'error', message: finalError.message }));
|
|
195
|
+
response.headers.set('Content-Type', 'application/json');
|
|
196
|
+
return response;
|
|
104
197
|
}
|
|
105
198
|
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Instantiate and run the service worker class
|
|
204
|
+
new PwaServiceWorker().run();
|
package/src/client.dev.js
CHANGED
package/src/index.js
CHANGED
package/src/proxy.js
CHANGED
|
@@ -23,6 +23,7 @@ import { applySecurity, authMiddlewareFactory } from '../../server/auth.js';
|
|
|
23
23
|
import { ssrMiddlewareFactory } from '../../server/ssr.js';
|
|
24
24
|
import { TLS } from '../../server/tls.js';
|
|
25
25
|
import { shellExec } from '../../server/process.js';
|
|
26
|
+
import { devProxyHostFactory, isDevProxyContext, isTlsDevProxy } from '../../server/conf.js';
|
|
26
27
|
|
|
27
28
|
const logger = loggerFactory(import.meta);
|
|
28
29
|
|
|
@@ -94,7 +95,9 @@ class ExpressService {
|
|
|
94
95
|
|
|
95
96
|
const app = express();
|
|
96
97
|
|
|
97
|
-
if (
|
|
98
|
+
if (origins && isDevProxyContext())
|
|
99
|
+
origins.push(devProxyHostFactory({ host, includeHttp: true, tls: isTlsDevProxy() }));
|
|
100
|
+
app.set('trust proxy', true);
|
|
98
101
|
|
|
99
102
|
app.use((req, res, next) => {
|
|
100
103
|
res.on('finish', () => {
|
package/src/server/auth.js
CHANGED
|
@@ -17,6 +17,7 @@ import slowDown from 'express-slow-down';
|
|
|
17
17
|
import cors from 'cors';
|
|
18
18
|
import cookieParser from 'cookie-parser';
|
|
19
19
|
import { DataBaseProvider } from '../db/DataBaseProvider.js';
|
|
20
|
+
import { isDevProxyContext } from './conf.js';
|
|
20
21
|
|
|
21
22
|
dotenv.config();
|
|
22
23
|
const logger = loggerFactory(import.meta);
|
|
@@ -348,7 +349,7 @@ const cookieOptionsFactory = (req, host) => {
|
|
|
348
349
|
secure,
|
|
349
350
|
sameSite,
|
|
350
351
|
path: '/',
|
|
351
|
-
domain: process.env.NODE_ENV === 'production' ? host : 'localhost',
|
|
352
|
+
domain: process.env.NODE_ENV === 'production' || isDevProxyContext() ? host : 'localhost',
|
|
352
353
|
maxAge,
|
|
353
354
|
};
|
|
354
355
|
|
package/src/server/conf.js
CHANGED
|
@@ -66,11 +66,11 @@ const Config = {
|
|
|
66
66
|
/**
|
|
67
67
|
* @method deployIdFactory
|
|
68
68
|
* @description Creates a new deploy ID.
|
|
69
|
-
* @param {string} [deployId='dd-default
|
|
70
|
-
* @param {object} [options={ cluster: false }] - The options.
|
|
69
|
+
* @param {string} [deployId='dd-default
|
|
70
|
+
* @param {object} [options={ subConf: '', cluster: false }] - The options.
|
|
71
71
|
* @memberof ServerConfBuilder
|
|
72
72
|
*/
|
|
73
|
-
deployIdFactory: function (deployId = 'dd-default', options = { cluster: false }) {
|
|
73
|
+
deployIdFactory: function (deployId = 'dd-default', options = { subConf: '', cluster: false }) {
|
|
74
74
|
if (!deployId.startsWith('dd-')) deployId = `dd-${deployId}`;
|
|
75
75
|
|
|
76
76
|
logger.info('Build deployId', deployId);
|
|
@@ -102,6 +102,17 @@ const Config = {
|
|
|
102
102
|
|
|
103
103
|
this.buildTmpConf(folder);
|
|
104
104
|
|
|
105
|
+
if (options.subConf) {
|
|
106
|
+
logger.info('Creating sub conf', {
|
|
107
|
+
deployId: deployId,
|
|
108
|
+
subConf: options.subConf,
|
|
109
|
+
});
|
|
110
|
+
fs.copySync(
|
|
111
|
+
`./engine-private/conf/${deployId}/conf.server.json`,
|
|
112
|
+
`./engine-private/conf/${deployId}/conf.server.dev.${options.subConf}.json`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
105
116
|
if (options.cluster === true) {
|
|
106
117
|
fs.writeFileSync(
|
|
107
118
|
`./.github/workflows/${repoName}.cd.yml`,
|
|
@@ -640,6 +651,8 @@ const buildProxyRouter = () => {
|
|
|
640
651
|
for (const path of Object.keys(confServer[host])) {
|
|
641
652
|
if (confServer[host][path].singleReplica) continue;
|
|
642
653
|
|
|
654
|
+
if (isDevProxyContext()) confServer[host][path].proxy = [isTlsDevProxy() ? 443 : 80];
|
|
655
|
+
|
|
643
656
|
confServer[host][path].port = newInstance(currentPort);
|
|
644
657
|
for (const port of confServer[host][path].proxy) {
|
|
645
658
|
if (!(port in proxyRouter)) proxyRouter[port] = {};
|
|
@@ -784,14 +797,21 @@ const buildKindPorts = (from, to) =>
|
|
|
784
797
|
/**
|
|
785
798
|
* @method buildPortProxyRouter
|
|
786
799
|
* @description Builds the port proxy router.
|
|
787
|
-
* @param {
|
|
788
|
-
* @param {
|
|
789
|
-
* @param {object}
|
|
800
|
+
* @param {object} options - The options.
|
|
801
|
+
* @param {number} [options.port=4000] - The port.
|
|
802
|
+
* @param {object} options.proxyRouter - The proxy router.
|
|
803
|
+
* @param {object} [options.hosts] - The hosts.
|
|
804
|
+
* @param {boolean} [options.orderByPathLength=false] - Whether to order by path length.
|
|
805
|
+
* @param {boolean} [options.devProxyContext=false] - Whether to use dev proxy context.
|
|
790
806
|
* @returns {object} - The port proxy router.
|
|
791
807
|
* @memberof ServerConfBuilder
|
|
792
808
|
*/
|
|
793
|
-
const buildPortProxyRouter = (
|
|
794
|
-
|
|
809
|
+
const buildPortProxyRouter = (
|
|
810
|
+
options = { port: 4000, proxyRouter, hosts, orderByPathLength: false, devProxyContext: false },
|
|
811
|
+
) => {
|
|
812
|
+
let { port, proxyRouter, hosts, orderByPathLength } = options;
|
|
813
|
+
hosts = hosts || proxyRouter[port] || {};
|
|
814
|
+
|
|
795
815
|
const router = {};
|
|
796
816
|
// build router
|
|
797
817
|
Object.keys(hosts).map((hostKey) => {
|
|
@@ -804,7 +824,7 @@ const buildPortProxyRouter = (port, proxyRouter, options = { orderByPathLength:
|
|
|
804
824
|
return;
|
|
805
825
|
}
|
|
806
826
|
}
|
|
807
|
-
|
|
827
|
+
// ${process.env.NODE_ENV === 'development' && !isDevProxyContext() ? `:${port}` : ''}
|
|
808
828
|
const absoluteHost = [80, 443].includes(port)
|
|
809
829
|
? `${host}${path === '/' ? '' : path}`
|
|
810
830
|
: `${host}:${port}${path === '/' ? '' : path}`;
|
|
@@ -812,12 +832,48 @@ const buildPortProxyRouter = (port, proxyRouter, options = { orderByPathLength:
|
|
|
812
832
|
if (absoluteHost in router)
|
|
813
833
|
logger.warn('Overwrite: Absolute host already exists on router', { absoluteHost, target });
|
|
814
834
|
|
|
815
|
-
|
|
835
|
+
if (options.devProxyContext === true) {
|
|
836
|
+
const appDevPort = parseInt(target.split(':')[2]) - process.env.DEV_PROXY_PORT_OFFSET;
|
|
837
|
+
router[absoluteHost] = `http://localhost:${appDevPort}`;
|
|
838
|
+
} else router[absoluteHost] = target;
|
|
816
839
|
}); // order router
|
|
817
840
|
|
|
818
841
|
if (Object.keys(router).length === 0) return router;
|
|
819
842
|
|
|
820
|
-
if (options.
|
|
843
|
+
if (options.devProxyContext === true && process.env.NODE_ENV === 'development') {
|
|
844
|
+
const confDevApiServer = JSON.parse(
|
|
845
|
+
fs.readFileSync(`./engine-private/conf/${process.argv[3]}/conf.server.dev.${process.argv[4]}-dev-api.json`),
|
|
846
|
+
'utf8',
|
|
847
|
+
);
|
|
848
|
+
let devApiHosts = [];
|
|
849
|
+
let origins = [];
|
|
850
|
+
for (const _host of Object.keys(confDevApiServer))
|
|
851
|
+
for (const _path of Object.keys(confDevApiServer[_host])) {
|
|
852
|
+
if (confDevApiServer[_host][_path].origins && confDevApiServer[_host][_path].origins.length) {
|
|
853
|
+
origins.push(...confDevApiServer[_host][_path].origins);
|
|
854
|
+
if (_path !== 'peer' && devApiHosts.length === 0)
|
|
855
|
+
devApiHosts.push(
|
|
856
|
+
`${_host}${[80, 443].includes(port) && isDevProxyContext() ? '' : `:${port}`}${_path == '/' ? '' : _path}`,
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
origins = Array.from(new Set(origins));
|
|
861
|
+
console.log({
|
|
862
|
+
origins,
|
|
863
|
+
devApiHosts,
|
|
864
|
+
});
|
|
865
|
+
for (const devApiHost of devApiHosts) {
|
|
866
|
+
if (devApiHost in router) {
|
|
867
|
+
const target = router[devApiHost];
|
|
868
|
+
delete router[devApiHost];
|
|
869
|
+
router[`${devApiHost}/${process.env.BASE_API}`] = target;
|
|
870
|
+
router[`${devApiHost}/socket.io`] = target;
|
|
871
|
+
for (const origin of origins) router[devApiHost] = origin;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
if (orderByPathLength === true) {
|
|
821
877
|
const reOrderRouter = {};
|
|
822
878
|
for (const absoluteHostKey of orderArrayFromAttrInt(Object.keys(router), 'length'))
|
|
823
879
|
reOrderRouter[absoluteHostKey] = router[absoluteHostKey];
|
|
@@ -1409,11 +1465,14 @@ const buildApiConf = async (options = { deployId: '', subConf: '', host: '', pat
|
|
|
1409
1465
|
* @param {string} options.apiBaseHost - The API base host.
|
|
1410
1466
|
* @param {string} options.host - The host.
|
|
1411
1467
|
* @param {string} options.path - The path.
|
|
1468
|
+
* @param {boolean} options.devProxy - The dev proxy flag.
|
|
1412
1469
|
* @returns {void}
|
|
1413
1470
|
* @memberof ServerConfBuilder
|
|
1414
1471
|
*/
|
|
1415
|
-
const buildClientStaticConf = async (
|
|
1416
|
-
|
|
1472
|
+
const buildClientStaticConf = async (
|
|
1473
|
+
options = { deployId: '', subConf: '', apiBaseHost: '', host: '', path: '', devProxy: false },
|
|
1474
|
+
) => {
|
|
1475
|
+
let { deployId, subConf, host, path, devProxy } = options;
|
|
1417
1476
|
if (!deployId) deployId = process.argv[2].trim();
|
|
1418
1477
|
if (!subConf) subConf = process.argv[3].trim();
|
|
1419
1478
|
if (!host) host = process.argv[4].trim();
|
|
@@ -1425,10 +1484,14 @@ const buildClientStaticConf = async (options = { deployId: '', subConf: '', apiB
|
|
|
1425
1484
|
fs.readFileSync(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-api`, 'utf8'),
|
|
1426
1485
|
);
|
|
1427
1486
|
envObj.PORT = parseInt(envObj.PORT);
|
|
1428
|
-
const apiBaseHost =
|
|
1487
|
+
const apiBaseHost = devProxy
|
|
1488
|
+
? devProxyHostFactory({ host, tls: isTlsDevProxy() })
|
|
1489
|
+
: options?.apiBaseHost
|
|
1490
|
+
? options.apiBaseHost
|
|
1491
|
+
: `localhost:${envObj.PORT + 1}`;
|
|
1429
1492
|
confServer[host][path].apiBaseHost = apiBaseHost;
|
|
1430
1493
|
confServer[host][path].apiBaseProxyPath = path;
|
|
1431
|
-
logger.
|
|
1494
|
+
logger.warn('Build client static conf', { host, path, apiBaseHost });
|
|
1432
1495
|
envObj.PORT = parseInt(confServer[host][path].origins[0].split(':')[2]) - 1;
|
|
1433
1496
|
writeEnv(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-client`, envObj);
|
|
1434
1497
|
fs.writeFileSync(
|
|
@@ -1448,6 +1511,56 @@ const buildClientStaticConf = async (options = { deployId: '', subConf: '', apiB
|
|
|
1448
1511
|
*/
|
|
1449
1512
|
const isDeployRunnerContext = (path, options) => !options.build && path && path !== 'template-deploy';
|
|
1450
1513
|
|
|
1514
|
+
/**
|
|
1515
|
+
* @method isDevProxyContext
|
|
1516
|
+
* @description Checks if the dev proxy context is valid.
|
|
1517
|
+
* @returns {boolean} - The dev proxy context.
|
|
1518
|
+
* @memberof ServerConfBuilder
|
|
1519
|
+
*/
|
|
1520
|
+
const isDevProxyContext = () => {
|
|
1521
|
+
try {
|
|
1522
|
+
if (process.argv[2] === 'proxy') return true;
|
|
1523
|
+
if (!process.argv[6].startsWith('localhost')) return false;
|
|
1524
|
+
return new URL('http://' + process.argv[6]).hostname ? true : false;
|
|
1525
|
+
} catch {
|
|
1526
|
+
return false;
|
|
1527
|
+
}
|
|
1528
|
+
};
|
|
1529
|
+
|
|
1530
|
+
/**
|
|
1531
|
+
* @method devProxyHostFactory
|
|
1532
|
+
* @description Creates the dev proxy host.
|
|
1533
|
+
* @param {object} options - The options.
|
|
1534
|
+
* @param {string} [options.host='default.net'] - The host.
|
|
1535
|
+
* @param {boolean} [options.includeHttp=false] - Whether to include HTTP.
|
|
1536
|
+
* @param {number} [options.port=443] - The port.
|
|
1537
|
+
* @param {boolean} [options.tls=false] - Whether to use TLS.
|
|
1538
|
+
* @returns {string} - The dev proxy host.
|
|
1539
|
+
* @memberof ServerConfBuilder
|
|
1540
|
+
*/
|
|
1541
|
+
const devProxyHostFactory = (options = { host: 'default.net', includeHttp: false, port: 80, tls: false }) =>
|
|
1542
|
+
`${options.includeHttp ? (options.tls ? 'https://' : 'http://') : ''}${options.host ? options.host : 'localhost'}:${
|
|
1543
|
+
(options.port ? options.port : options.tls ? 443 : 80) + parseInt(process.env.DEV_PROXY_PORT_OFFSET)
|
|
1544
|
+
}`;
|
|
1545
|
+
|
|
1546
|
+
/**
|
|
1547
|
+
* @method isTlsDevProxy
|
|
1548
|
+
* @description Checks if TLS is used in the dev proxy.
|
|
1549
|
+
* @returns {boolean} - The TLS dev proxy status.
|
|
1550
|
+
* @memberof ServerConfBuilder
|
|
1551
|
+
*/
|
|
1552
|
+
const isTlsDevProxy = () => process.env.NODE_ENV !== 'production' && process.argv[7] === 'tls';
|
|
1553
|
+
|
|
1554
|
+
/**
|
|
1555
|
+
* @method getTlsHosts
|
|
1556
|
+
* @description Gets the TLS hosts.
|
|
1557
|
+
* @param {object} confServer - The server configuration.
|
|
1558
|
+
* @returns {Array} - The TLS hosts.
|
|
1559
|
+
* @memberof ServerConfBuilder
|
|
1560
|
+
*/
|
|
1561
|
+
const getTlsHosts = (confServer) =>
|
|
1562
|
+
Array.from(new Set(Object.keys(confServer).map((h) => new URL('https://' + h).hostname)));
|
|
1563
|
+
|
|
1451
1564
|
export {
|
|
1452
1565
|
Cmd,
|
|
1453
1566
|
Config,
|
|
@@ -1484,4 +1597,8 @@ export {
|
|
|
1484
1597
|
buildApiConf,
|
|
1485
1598
|
buildClientStaticConf,
|
|
1486
1599
|
isDeployRunnerContext,
|
|
1600
|
+
isDevProxyContext,
|
|
1601
|
+
devProxyHostFactory,
|
|
1602
|
+
isTlsDevProxy,
|
|
1603
|
+
getTlsHosts,
|
|
1487
1604
|
};
|