@underpostnet/underpost 3.0.3 → 3.1.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.production → .env.example} +20 -2
- package/.github/workflows/ghpkg.ci.yml +1 -1
- package/.github/workflows/gitlab.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +22 -7
- package/.github/workflows/publish.ci.yml +5 -5
- package/.github/workflows/pwa-microservices-template-page.cd.yml +3 -3
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/.github/workflows/release.cd.yml +3 -2
- package/.vscode/extensions.json +9 -8
- package/.vscode/settings.json +3 -2
- package/CHANGELOG.md +146 -1
- package/CLI-HELP.md +71 -52
- package/README.md +2 -2
- package/bin/build.js +4 -1
- package/bin/deploy.js +150 -208
- package/bin/file.js +2 -1
- package/bin/vs.js +3 -3
- package/conf.js +30 -13
- 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 +52 -52
- package/manifests/deployment/dd-test-development/proxy.yaml +4 -4
- package/manifests/pv-pvc-dd.yaml +1 -1
- package/package.json +48 -43
- package/scripts/k3s-node-setup.sh +1 -1
- package/src/api/document/document.service.js +1 -1
- package/src/api/file/file.controller.js +3 -1
- package/src/api/file/file.service.js +28 -5
- package/src/api/user/user.router.js +10 -5
- package/src/api/user/user.service.js +7 -7
- package/src/cli/baremetal.js +6 -10
- package/src/cli/cloud-init.js +0 -3
- package/src/cli/db.js +54 -71
- package/src/cli/deploy.js +64 -12
- package/src/cli/env.js +4 -4
- package/src/cli/fs.js +0 -2
- package/src/cli/image.js +0 -3
- package/src/cli/index.js +27 -13
- package/src/cli/monitor.js +5 -6
- package/src/cli/repository.js +322 -35
- package/src/cli/run.js +118 -69
- package/src/cli/secrets.js +0 -3
- package/src/cli/ssh.js +1 -1
- package/src/client/components/core/AgGrid.js +20 -5
- package/src/client/components/core/Content.js +22 -3
- package/src/client/components/core/Docs.js +21 -4
- package/src/client/components/core/FileExplorer.js +71 -4
- package/src/client/components/core/Input.js +1 -1
- package/src/client/components/core/Modal.js +20 -6
- package/src/client/public/default/sitemap +3 -3
- package/src/client/public/test/sitemap +3 -3
- package/src/client.build.js +0 -3
- package/src/client.dev.js +0 -3
- package/src/db/DataBaseProvider.js +17 -2
- package/src/db/mariadb/MariaDB.js +14 -9
- package/src/db/mongo/MongooseDB.js +17 -1
- package/src/index.js +1 -1
- package/src/proxy.js +0 -3
- package/src/runtime/express/Express.js +7 -1
- package/src/runtime/lampp/Lampp.js +6 -13
- package/src/server/auth.js +6 -9
- package/src/server/backup.js +2 -3
- package/src/server/client-build-docs.js +178 -3
- package/src/server/client-build-live.js +9 -18
- package/src/server/client-build.js +175 -38
- package/src/server/client-dev-server.js +14 -13
- package/src/server/conf.js +357 -149
- package/src/server/cron.js +2 -1
- package/src/server/dns.js +28 -12
- package/src/server/downloader.js +0 -2
- package/src/server/logger.js +27 -9
- package/src/server/peer.js +0 -2
- package/src/server/process.js +1 -50
- package/src/server/proxy.js +4 -8
- package/src/server/runtime.js +5 -8
- package/src/server/ssr.js +0 -3
- package/src/server/start.js +5 -5
- package/src/server/tls.js +0 -2
- package/src/server.js +0 -4
- package/.env.development +0 -43
- package/.env.test +0 -43
|
@@ -59,6 +59,10 @@ const buildApiDocs = async ({
|
|
|
59
59
|
name: 'user',
|
|
60
60
|
description: 'User API operations',
|
|
61
61
|
},
|
|
62
|
+
{
|
|
63
|
+
name: 'object-layer',
|
|
64
|
+
description: 'Object Layer API operations',
|
|
65
|
+
},
|
|
62
66
|
],
|
|
63
67
|
components: {
|
|
64
68
|
schemas: {
|
|
@@ -150,6 +154,70 @@ const buildApiDocs = async ({
|
|
|
150
154
|
},
|
|
151
155
|
},
|
|
152
156
|
},
|
|
157
|
+
objectLayerResponse: {
|
|
158
|
+
type: 'object',
|
|
159
|
+
properties: {
|
|
160
|
+
status: { type: 'string', example: 'success' },
|
|
161
|
+
data: {
|
|
162
|
+
type: 'object',
|
|
163
|
+
properties: {
|
|
164
|
+
_id: { type: 'string', example: '66c377f57f99e5969b81de89' },
|
|
165
|
+
data: {
|
|
166
|
+
type: 'object',
|
|
167
|
+
properties: {
|
|
168
|
+
stats: {
|
|
169
|
+
type: 'object',
|
|
170
|
+
properties: {
|
|
171
|
+
effect: { type: 'number', example: 0 },
|
|
172
|
+
resistance: { type: 'number', example: 0 },
|
|
173
|
+
agility: { type: 'number', example: 0 },
|
|
174
|
+
range: { type: 'number', example: 0 },
|
|
175
|
+
intelligence: { type: 'number', example: 0 },
|
|
176
|
+
utility: { type: 'number', example: 0 },
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
item: {
|
|
180
|
+
type: 'object',
|
|
181
|
+
properties: {
|
|
182
|
+
id: { type: 'string', example: 'skin-default' },
|
|
183
|
+
type: { type: 'string', example: 'skin' },
|
|
184
|
+
description: { type: 'string', example: 'Default skin layer' },
|
|
185
|
+
activable: { type: 'boolean', example: false },
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
ledger: {
|
|
189
|
+
type: 'object',
|
|
190
|
+
properties: {
|
|
191
|
+
type: { type: 'string', example: 'semi-fungible' },
|
|
192
|
+
address: { type: 'string', example: '0x0000000000000000000000000000000000000000' },
|
|
193
|
+
tokenId: { type: 'string', example: '' },
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
render: {
|
|
197
|
+
type: 'object',
|
|
198
|
+
properties: {
|
|
199
|
+
cid: { type: 'string', example: '' },
|
|
200
|
+
metadataCid: { type: 'string', example: '' },
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
cid: { type: 'string', example: '' },
|
|
206
|
+
sha256: { type: 'string', example: 'abc123def456...' },
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
objectLayerBadRequestResponse: {
|
|
212
|
+
type: 'object',
|
|
213
|
+
properties: {
|
|
214
|
+
status: { type: 'string', example: 'error' },
|
|
215
|
+
message: {
|
|
216
|
+
type: 'string',
|
|
217
|
+
example: 'Bad request. Please check your inputs, and try again',
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
},
|
|
153
221
|
securitySchemes: {
|
|
154
222
|
bearerAuth: {
|
|
155
223
|
type: 'http',
|
|
@@ -221,7 +289,7 @@ const buildApiDocs = async ({
|
|
|
221
289
|
const outputFile = `./public/${host}${path === '/' ? path : `${path}/`}swagger-output.json`;
|
|
222
290
|
const routes = [];
|
|
223
291
|
for (const api of apis) {
|
|
224
|
-
if (['user'].includes(api)) routes.push(`./src/api/${api}/${api}.router.js`);
|
|
292
|
+
if (['user', 'object-layer'].includes(api)) routes.push(`./src/api/${api}/${api}.router.js`);
|
|
225
293
|
}
|
|
226
294
|
|
|
227
295
|
await swaggerAutoGen({ openapi: '3.0.0' })(outputFile, routes, doc);
|
|
@@ -267,14 +335,82 @@ const buildApiDocs = async ({
|
|
|
267
335
|
*/
|
|
268
336
|
const buildJsDocs = async ({ host, path, metadata = {}, publicClientId }) => {
|
|
269
337
|
const logger = loggerFactory(import.meta);
|
|
270
|
-
|
|
338
|
+
|
|
339
|
+
// Detect custom jsdoc.<deployId>.json by matching host against deploy server configs
|
|
340
|
+
let customJsDocPath = '';
|
|
341
|
+
const privateConfBase = `./engine-private/conf`;
|
|
342
|
+
if (fs.existsSync(privateConfBase)) {
|
|
343
|
+
for (const deployId of fs.readdirSync(privateConfBase)) {
|
|
344
|
+
const candidatePath = `./jsdoc.${deployId}.json`;
|
|
345
|
+
if (!fs.existsSync(candidatePath)) continue;
|
|
346
|
+
|
|
347
|
+
// Check if this deployId's server config contains the current host
|
|
348
|
+
const serverConfPath = `${privateConfBase}/${deployId}/conf.server.json`;
|
|
349
|
+
if (fs.existsSync(serverConfPath)) {
|
|
350
|
+
try {
|
|
351
|
+
const serverConf = JSON.parse(fs.readFileSync(serverConfPath, 'utf8'));
|
|
352
|
+
if (serverConf[host]) {
|
|
353
|
+
customJsDocPath = candidatePath;
|
|
354
|
+
logger.info('detected custom jsdoc config', { deployId, host, path: candidatePath });
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
} catch (e) {
|
|
358
|
+
// skip invalid JSON
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Fallback: also check dev server configs
|
|
363
|
+
if (!customJsDocPath) {
|
|
364
|
+
const devConfFiles = fs
|
|
365
|
+
.readdirSync(`${privateConfBase}/${deployId}`)
|
|
366
|
+
.filter((f) => f.match(/^conf\.server\.dev\..*\.json$/));
|
|
367
|
+
for (const devFile of devConfFiles) {
|
|
368
|
+
try {
|
|
369
|
+
const devConf = JSON.parse(fs.readFileSync(`${privateConfBase}/${deployId}/${devFile}`, 'utf8'));
|
|
370
|
+
if (devConf[host]) {
|
|
371
|
+
customJsDocPath = candidatePath;
|
|
372
|
+
logger.info('detected custom jsdoc config (dev)', { deployId, host, path: candidatePath });
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
} catch (e) {
|
|
376
|
+
// skip invalid JSON
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (customJsDocPath) break;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const jsDocSourcePath = customJsDocPath || `./jsdoc.json`;
|
|
385
|
+
const jsDocsConfig = JSON.parse(fs.readFileSync(jsDocSourcePath, 'utf8'));
|
|
386
|
+
logger.info('using jsdoc config', jsDocSourcePath);
|
|
271
387
|
|
|
272
388
|
jsDocsConfig.opts.destination = `./public/${host}${path === '/' ? path : `${path}/`}docs/`;
|
|
273
389
|
jsDocsConfig.opts.theme_opts.title = metadata?.title ? metadata.title : undefined;
|
|
274
390
|
jsDocsConfig.opts.theme_opts.favicon = `./public/${host}${path === '/' ? '/' : `${path}/`}favicon.ico`;
|
|
275
391
|
|
|
276
392
|
const tutorialsPath = `./src/client/public/${publicClientId}/docs/references`;
|
|
277
|
-
|
|
393
|
+
|
|
394
|
+
// Auto-prepare hardhat references when jsdoc config includes hardhat source files
|
|
395
|
+
const includesHardhat =
|
|
396
|
+
jsDocsConfig.source &&
|
|
397
|
+
Array.isArray(jsDocsConfig.source.include) &&
|
|
398
|
+
jsDocsConfig.source.include.some((p) => p.includes('hardhat/'));
|
|
399
|
+
if (includesHardhat && fs.existsSync(`./hardhat`)) {
|
|
400
|
+
fs.mkdirSync(tutorialsPath, { recursive: true });
|
|
401
|
+
const hardhatReadmePath = `./hardhat/README.md`;
|
|
402
|
+
const hardhatWhitePaperPath = `./hardhat/WHITE-PAPER.md`;
|
|
403
|
+
if (fs.existsSync(hardhatReadmePath)) {
|
|
404
|
+
fs.copySync(hardhatReadmePath, `${tutorialsPath}/Hardhat Module.md`);
|
|
405
|
+
logger.info('copied hardhat README.md to tutorials references');
|
|
406
|
+
}
|
|
407
|
+
if (fs.existsSync(hardhatWhitePaperPath)) {
|
|
408
|
+
fs.copySync(hardhatWhitePaperPath, `${tutorialsPath}/White Paper.md`);
|
|
409
|
+
logger.info('copied hardhat WHITE-PAPER.md to tutorials references');
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (fs.existsSync(tutorialsPath) && fs.readdirSync(tutorialsPath).length > 0) {
|
|
278
414
|
jsDocsConfig.opts.tutorials = tutorialsPath;
|
|
279
415
|
if (jsDocsConfig.opts.theme_opts.sections && !jsDocsConfig.opts.theme_opts.sections.includes('Tutorials')) {
|
|
280
416
|
jsDocsConfig.opts.theme_opts.sections.push('Tutorials');
|
|
@@ -311,6 +447,45 @@ const buildCoverage = async ({ host, path }) => {
|
|
|
311
447
|
fs.copySync(`./coverage`, coverageBuildPath);
|
|
312
448
|
|
|
313
449
|
logger.warn('build coverage', coverageBuildPath);
|
|
450
|
+
|
|
451
|
+
// Include hardhat coverage for cyberia-related builds
|
|
452
|
+
const hardhatCoveragePath = `./hardhat/coverage`;
|
|
453
|
+
if (fs.existsSync(hardhatCoveragePath) && fs.readdirSync(hardhatCoveragePath).length > 0) {
|
|
454
|
+
const hardhatCoverageBuildPath = `${jsDocsConfig.opts.destination}hardhat-coverage`;
|
|
455
|
+
fs.mkdirSync(hardhatCoverageBuildPath, { recursive: true });
|
|
456
|
+
fs.copySync(hardhatCoveragePath, hardhatCoverageBuildPath);
|
|
457
|
+
logger.warn('build hardhat coverage', hardhatCoverageBuildPath);
|
|
458
|
+
} else if (fs.existsSync(`./hardhat/package.json`)) {
|
|
459
|
+
// Attempt to generate hardhat coverage if the hardhat project exists
|
|
460
|
+
try {
|
|
461
|
+
const hardhatPkg = JSON.parse(fs.readFileSync(`./hardhat/package.json`, 'utf8'));
|
|
462
|
+
if (hardhatPkg.scripts && hardhatPkg.scripts.coverage) {
|
|
463
|
+
logger.info('generating hardhat coverage report');
|
|
464
|
+
shellExec(`cd ./hardhat && npx hardhat coverage`, { silent: true });
|
|
465
|
+
if (fs.existsSync(hardhatCoveragePath) && fs.readdirSync(hardhatCoveragePath).length > 0) {
|
|
466
|
+
const hardhatCoverageBuildPath = `${jsDocsConfig.opts.destination}hardhat-coverage`;
|
|
467
|
+
fs.mkdirSync(hardhatCoverageBuildPath, { recursive: true });
|
|
468
|
+
fs.copySync(hardhatCoveragePath, hardhatCoverageBuildPath);
|
|
469
|
+
logger.warn('build hardhat coverage (generated)', hardhatCoverageBuildPath);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
} catch (e) {
|
|
473
|
+
logger.warn('hardhat coverage generation skipped', e.message);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Copy hardhat README and WHITE-PAPER as markdown docs
|
|
478
|
+
const docsDestBase = jsDocsConfig.opts.destination;
|
|
479
|
+
const hardhatReadmePath = `./hardhat/README.md`;
|
|
480
|
+
const hardhatWhitePaperPath = `./hardhat/WHITE-PAPER.md`;
|
|
481
|
+
if (fs.existsSync(hardhatReadmePath)) {
|
|
482
|
+
fs.copySync(hardhatReadmePath, `${docsDestBase}hardhat-README.md`);
|
|
483
|
+
logger.info('copied hardhat README.md to docs');
|
|
484
|
+
}
|
|
485
|
+
if (fs.existsSync(hardhatWhitePaperPath)) {
|
|
486
|
+
fs.copySync(hardhatWhitePaperPath, `${docsDestBase}hardhat-WHITE-PAPER.md`);
|
|
487
|
+
logger.info('copied hardhat WHITE-PAPER.md to docs');
|
|
488
|
+
}
|
|
314
489
|
};
|
|
315
490
|
|
|
316
491
|
/**
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import fs from 'fs-extra';
|
|
8
|
-
import { Config, loadConf } from './conf.js';
|
|
8
|
+
import { Config, loadConf, readConfJson } from './conf.js';
|
|
9
9
|
import { loggerFactory } from './logger.js';
|
|
10
10
|
import { buildClient } from './client-build.js';
|
|
11
11
|
|
|
@@ -17,14 +17,14 @@ const logger = loggerFactory(import.meta);
|
|
|
17
17
|
* @memberof clientLiveBuild
|
|
18
18
|
*/
|
|
19
19
|
const clientLiveBuild = async () => {
|
|
20
|
-
if (fs.existsSync(
|
|
20
|
+
if (fs.existsSync(`/tmp/client.build.json`)) {
|
|
21
21
|
const deployId = process.argv[2];
|
|
22
22
|
const subConf = process.argv[3];
|
|
23
23
|
let clientId = 'default';
|
|
24
24
|
let host = 'default.net';
|
|
25
25
|
let path = '/';
|
|
26
26
|
let baseHost = `${host}${path === '/' ? '' : path}`;
|
|
27
|
-
let views
|
|
27
|
+
let views;
|
|
28
28
|
let apiBaseHost;
|
|
29
29
|
let apiBaseProxyPath;
|
|
30
30
|
|
|
@@ -33,19 +33,8 @@ const clientLiveBuild = async () => {
|
|
|
33
33
|
(fs.existsSync(`./engine-private/conf/${deployId}`) || fs.existsSync(`./engine-private/replica/${deployId}`))
|
|
34
34
|
) {
|
|
35
35
|
loadConf(deployId, subConf);
|
|
36
|
-
const confClient =
|
|
37
|
-
|
|
38
|
-
fs.existsSync(`./engine-private/replica/${deployId}`)
|
|
39
|
-
? `./engine-private/replica/${deployId}/conf.client.json`
|
|
40
|
-
: fs.existsSync(`./engine-private/conf/${deployId}/conf.client.json`)
|
|
41
|
-
? `./engine-private/conf/${deployId}/conf.client.json`
|
|
42
|
-
: `./conf/conf.client.json`,
|
|
43
|
-
'utf8',
|
|
44
|
-
),
|
|
45
|
-
);
|
|
46
|
-
const confServer = JSON.parse(
|
|
47
|
-
fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.dev.${subConf}.json`, 'utf8'),
|
|
48
|
-
);
|
|
36
|
+
const confClient = readConfJson(deployId, 'client');
|
|
37
|
+
const confServer = readConfJson(deployId, 'server');
|
|
49
38
|
host = process.argv[4];
|
|
50
39
|
path = process.argv[5];
|
|
51
40
|
clientId = confServer[host][path].client;
|
|
@@ -53,6 +42,8 @@ const clientLiveBuild = async () => {
|
|
|
53
42
|
baseHost = `${host}${path === '/' ? '' : path}`;
|
|
54
43
|
apiBaseHost = confServer[host][path].apiBaseHost;
|
|
55
44
|
apiBaseProxyPath = confServer[host][path].apiBaseProxyPath;
|
|
45
|
+
} else {
|
|
46
|
+
views = Config.default.client[clientId].views;
|
|
56
47
|
}
|
|
57
48
|
|
|
58
49
|
logger.info('Live build config', {
|
|
@@ -67,7 +58,7 @@ const clientLiveBuild = async () => {
|
|
|
67
58
|
apiBaseProxyPath,
|
|
68
59
|
});
|
|
69
60
|
|
|
70
|
-
const updates = JSON.parse(fs.readFileSync(
|
|
61
|
+
const updates = JSON.parse(fs.readFileSync(`/tmp/client.build.json`, 'utf8'));
|
|
71
62
|
const liveClientBuildPaths = [];
|
|
72
63
|
for (let srcPath of updates) {
|
|
73
64
|
srcPath = srcPath.replaceAll('/', `\\`);
|
|
@@ -102,7 +93,7 @@ const clientLiveBuild = async () => {
|
|
|
102
93
|
}
|
|
103
94
|
logger.info('liveClientBuildPaths', liveClientBuildPaths);
|
|
104
95
|
await buildClient({ liveClientBuildPaths, instances: [{ host, path }] });
|
|
105
|
-
fs.removeSync(
|
|
96
|
+
fs.removeSync(`/tmp/client.build.json`);
|
|
106
97
|
}
|
|
107
98
|
};
|
|
108
99
|
|
|
@@ -15,9 +15,9 @@ import {
|
|
|
15
15
|
orderArrayFromAttrInt,
|
|
16
16
|
uniqueArray,
|
|
17
17
|
} from '../client/components/core/CommonJs.js';
|
|
18
|
+
import { readConfJson } from './conf.js';
|
|
18
19
|
import UglifyJS from 'uglify-js';
|
|
19
20
|
import { minify } from 'html-minifier-terser';
|
|
20
|
-
import dotenv from 'dotenv';
|
|
21
21
|
import AdmZip from 'adm-zip';
|
|
22
22
|
import * as dir from 'path';
|
|
23
23
|
import { shellExec } from './process.js';
|
|
@@ -28,8 +28,6 @@ import Underpost from '../index.js';
|
|
|
28
28
|
import { buildDocs } from './client-build-docs.js';
|
|
29
29
|
import { ssrFactory } from './ssr.js';
|
|
30
30
|
|
|
31
|
-
dotenv.config();
|
|
32
|
-
|
|
33
31
|
// Static Site Generation (SSG)
|
|
34
32
|
|
|
35
33
|
/**
|
|
@@ -77,6 +75,156 @@ const copyNonExistingFiles = (src, dest) => {
|
|
|
77
75
|
}
|
|
78
76
|
};
|
|
79
77
|
|
|
78
|
+
/** @type {string} Default XSL sitemap template used when no `sitemap` source file exists in the public directory. */
|
|
79
|
+
const defaultSitemapXsl = `<?xml version="1.0" encoding="UTF-8"?>
|
|
80
|
+
<xsl:stylesheet version="1.0"
|
|
81
|
+
xmlns:html="http://www.w3.org/TR/REC-html40"
|
|
82
|
+
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
|
|
83
|
+
xmlns:sitemap="http://www.sitemaps.org/schemas/sitemap/0.9"
|
|
84
|
+
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
85
|
+
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes" />
|
|
86
|
+
<xsl:template match="/">
|
|
87
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
88
|
+
<head>
|
|
89
|
+
<title>XML Sitemap</title>
|
|
90
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
91
|
+
<style type="text/css">
|
|
92
|
+
body {
|
|
93
|
+
font-family: sans-serif;
|
|
94
|
+
font-size: 16px;
|
|
95
|
+
color: #242628;
|
|
96
|
+
}
|
|
97
|
+
a {
|
|
98
|
+
color: #000;
|
|
99
|
+
text-decoration: none;
|
|
100
|
+
}
|
|
101
|
+
a:hover {
|
|
102
|
+
text-decoration: underline;
|
|
103
|
+
}
|
|
104
|
+
table {
|
|
105
|
+
border: none;
|
|
106
|
+
border-collapse: collapse;
|
|
107
|
+
width: 100%
|
|
108
|
+
}
|
|
109
|
+
th {
|
|
110
|
+
text-align: left;
|
|
111
|
+
padding-right: 30px;
|
|
112
|
+
font-size: 11px;
|
|
113
|
+
}
|
|
114
|
+
thead th {
|
|
115
|
+
border-bottom: 1px solid #7d878a;
|
|
116
|
+
cursor: pointer;
|
|
117
|
+
}
|
|
118
|
+
td {
|
|
119
|
+
font-size:11px;
|
|
120
|
+
padding: 5px;
|
|
121
|
+
}
|
|
122
|
+
tr:nth-child(odd) td {
|
|
123
|
+
background-color: rgba(0,0,0,0.04);
|
|
124
|
+
}
|
|
125
|
+
tr:hover td {
|
|
126
|
+
background-color: #e2edf2;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
#content {
|
|
130
|
+
margin: 0 auto;
|
|
131
|
+
padding: 2% 5%;
|
|
132
|
+
max-width: 800px;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.desc {
|
|
136
|
+
margin: 18px 3px;
|
|
137
|
+
line-height: 1.2em;
|
|
138
|
+
}
|
|
139
|
+
.desc a {
|
|
140
|
+
color: #5ba4e5;
|
|
141
|
+
}
|
|
142
|
+
</style>
|
|
143
|
+
</head>
|
|
144
|
+
<body>
|
|
145
|
+
<div id="content">
|
|
146
|
+
<h1>XML Sitemap</h1>
|
|
147
|
+
<p class="desc"> This is a sitemap generated by <a
|
|
148
|
+
href="{{web-url}}">{{web-url}}</a>
|
|
149
|
+
</p>
|
|
150
|
+
<xsl:if test="count(sitemap:sitemapindex/sitemap:sitemap) > 0">
|
|
151
|
+
<table id="sitemap" cellpadding="3">
|
|
152
|
+
<thead>
|
|
153
|
+
<tr>
|
|
154
|
+
<th width="75%">Sitemap</th>
|
|
155
|
+
<th width="25%">Last Modified</th>
|
|
156
|
+
</tr>
|
|
157
|
+
</thead>
|
|
158
|
+
<tbody>
|
|
159
|
+
<xsl:for-each select="sitemap:sitemapindex/sitemap:sitemap">
|
|
160
|
+
<xsl:variable name="sitemapURL">
|
|
161
|
+
<xsl:value-of select="sitemap:loc" />
|
|
162
|
+
</xsl:variable>
|
|
163
|
+
<tr>
|
|
164
|
+
<td>
|
|
165
|
+
<a href="{$sitemapURL}">
|
|
166
|
+
<xsl:value-of select="sitemap:loc" />
|
|
167
|
+
</a>
|
|
168
|
+
</td>
|
|
169
|
+
<td>
|
|
170
|
+
<xsl:value-of
|
|
171
|
+
select="concat(substring(sitemap:lastmod,0,11),concat(' ', substring(sitemap:lastmod,12,5)))" />
|
|
172
|
+
</td>
|
|
173
|
+
</tr>
|
|
174
|
+
</xsl:for-each>
|
|
175
|
+
</tbody>
|
|
176
|
+
</table>
|
|
177
|
+
</xsl:if>
|
|
178
|
+
<xsl:if test="count(sitemap:sitemapindex/sitemap:sitemap) < 1">
|
|
179
|
+
<p class="desc">
|
|
180
|
+
<a href="{{web-url}}sitemap.xml" class="back-link">← Back to index</a>
|
|
181
|
+
</p>
|
|
182
|
+
<table
|
|
183
|
+
id="sitemap" cellpadding="3">
|
|
184
|
+
<thead>
|
|
185
|
+
<tr>
|
|
186
|
+
<th width="70%">URL (<xsl:value-of
|
|
187
|
+
select="count(sitemap:urlset/sitemap:url)" /> total)</th>
|
|
188
|
+
<th width="15%">Images</th>
|
|
189
|
+
<th title="Last Modification Time" width="15%">Last Modified</th>
|
|
190
|
+
</tr>
|
|
191
|
+
</thead>
|
|
192
|
+
<tbody>
|
|
193
|
+
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'" />
|
|
194
|
+
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
|
|
195
|
+
<xsl:for-each select="sitemap:urlset/sitemap:url">
|
|
196
|
+
<tr>
|
|
197
|
+
<td>
|
|
198
|
+
<xsl:variable name="itemURL">
|
|
199
|
+
<xsl:value-of select="sitemap:loc" />
|
|
200
|
+
</xsl:variable>
|
|
201
|
+
<a href="{$itemURL}">
|
|
202
|
+
<xsl:value-of select="sitemap:loc" />
|
|
203
|
+
</a>
|
|
204
|
+
</td>
|
|
205
|
+
<td>
|
|
206
|
+
<xsl:value-of select="count(image:image)" />
|
|
207
|
+
</td>
|
|
208
|
+
<td>
|
|
209
|
+
<xsl:value-of
|
|
210
|
+
select="concat(substring(sitemap:lastmod,0,11),concat(' ', substring(sitemap:lastmod,12,5)))" />
|
|
211
|
+
</td>
|
|
212
|
+
</tr>
|
|
213
|
+
</xsl:for-each>
|
|
214
|
+
</tbody>
|
|
215
|
+
</table>
|
|
216
|
+
<p
|
|
217
|
+
class="desc">
|
|
218
|
+
<a href="{{web-url}}sitemap.xml" class="back-link">← Back to index</a>
|
|
219
|
+
</p>
|
|
220
|
+
</xsl:if>
|
|
221
|
+
</div>
|
|
222
|
+
</body>
|
|
223
|
+
</html>
|
|
224
|
+
|
|
225
|
+
</xsl:template>
|
|
226
|
+
</xsl:stylesheet>`;
|
|
227
|
+
|
|
80
228
|
/**
|
|
81
229
|
* @async
|
|
82
230
|
* @function buildClient
|
|
@@ -91,9 +239,10 @@ const copyNonExistingFiles = (src, dest) => {
|
|
|
91
239
|
*/
|
|
92
240
|
const buildClient = async (options = { liveClientBuildPaths: [], instances: [], buildZip: false }) => {
|
|
93
241
|
const logger = loggerFactory(import.meta);
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
const
|
|
242
|
+
const deployId = process.env.DEPLOY_ID;
|
|
243
|
+
const confClient = readConfJson(deployId, 'client');
|
|
244
|
+
const confServer = readConfJson(deployId, 'server', { loadReplicas: true });
|
|
245
|
+
const confSSR = readConfJson(deployId, 'ssr');
|
|
97
246
|
const packageData = JSON.parse(fs.readFileSync(`./package.json`, 'utf8'));
|
|
98
247
|
const acmeChallengePath = `/.well-known/acme-challenge`;
|
|
99
248
|
const publicPath = `./public`;
|
|
@@ -178,26 +327,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [],
|
|
|
178
327
|
} */,
|
|
179
328
|
);
|
|
180
329
|
} else if (fs.existsSync(`./engine-private/src/client/public/${publicClientId}`)) {
|
|
181
|
-
|
|
182
|
-
case 'mysql_test':
|
|
183
|
-
if (db) {
|
|
184
|
-
fs.copySync(`./engine-private/src/client/public/${publicClientId}`, rootClientPath);
|
|
185
|
-
fs.writeFileSync(
|
|
186
|
-
`${rootClientPath}/index.php`,
|
|
187
|
-
fs
|
|
188
|
-
.readFileSync(`${rootClientPath}/index.php`, 'utf8')
|
|
189
|
-
.replace('test_servername', 'localhost')
|
|
190
|
-
.replace('test_username', db.user)
|
|
191
|
-
.replace('test_password', db.password)
|
|
192
|
-
.replace('test_dbname', db.name),
|
|
193
|
-
'utf8',
|
|
194
|
-
);
|
|
195
|
-
} else logger.error('not provided db config');
|
|
196
|
-
break;
|
|
197
|
-
|
|
198
|
-
default:
|
|
199
|
-
break;
|
|
200
|
-
}
|
|
330
|
+
fs.copySync(`./engine-private/src/client/public/${publicClientId}`, rootClientPath);
|
|
201
331
|
}
|
|
202
332
|
if (dists)
|
|
203
333
|
for (const dist of dists) {
|
|
@@ -573,31 +703,38 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [],
|
|
|
573
703
|
}
|
|
574
704
|
}
|
|
575
705
|
if (!enableLiveRebuild && siteMapLinks.length > 0) {
|
|
576
|
-
const
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
// Create a stream to write to
|
|
706
|
+
const hasSitemapTemplate = fs.existsSync(`${rootClientPath}/sitemap`);
|
|
707
|
+
const sitemapBaseUrl = `https://${host}${path === '/' ? '' : path}`;
|
|
708
|
+
// Create a stream to write to — omit xslUrl so we can inject a relative href below
|
|
580
709
|
/** @type {import('sitemap').SitemapStreamOptions} */
|
|
581
|
-
const sitemapOptions = { hostname: `https://${host}
|
|
710
|
+
const sitemapOptions = { hostname: `https://${host}` };
|
|
582
711
|
|
|
583
712
|
const siteMapStream = new SitemapStream(sitemapOptions);
|
|
584
713
|
let siteMapSrc = await new Promise((resolve) =>
|
|
585
714
|
streamToPromise(Readable.from(siteMapLinks).pipe(siteMapStream)).then((data) => resolve(data.toString())),
|
|
586
715
|
);
|
|
587
716
|
|
|
717
|
+
// Inject a relative xml-stylesheet PI so the XSL loads from the same origin
|
|
718
|
+
// (works on both http://localhost:<port> and https://production-host)
|
|
719
|
+
siteMapSrc = siteMapSrc.replace(
|
|
720
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
721
|
+
'<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="sitemap.xsl"?>',
|
|
722
|
+
);
|
|
723
|
+
|
|
588
724
|
// Return a promise that resolves with your XML string
|
|
589
725
|
fs.writeFileSync(`${rootClientPath}/sitemap.xml`, siteMapSrc, 'utf8');
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
726
|
+
|
|
727
|
+
// Generate XSL stylesheet from source template or default fallback
|
|
728
|
+
const xslTemplate = hasSitemapTemplate
|
|
729
|
+
? fs.readFileSync(`${rootClientPath}/sitemap`, 'utf8')
|
|
730
|
+
: defaultSitemapXsl;
|
|
731
|
+
const webUrl = `https://${host}${path === '/' ? '/' : `${path}/`}`;
|
|
732
|
+
fs.writeFileSync(`${rootClientPath}/sitemap.xsl`, xslTemplate.replaceAll('{{web-url}}', webUrl), 'utf8');
|
|
596
733
|
|
|
597
734
|
fs.writeFileSync(
|
|
598
735
|
`${rootClientPath}/robots.txt`,
|
|
599
736
|
`User-agent: *
|
|
600
|
-
Sitemap:
|
|
737
|
+
Sitemap: ${sitemapBaseUrl}/sitemap.xml`,
|
|
601
738
|
'utf8',
|
|
602
739
|
);
|
|
603
740
|
}
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import fs from 'fs-extra';
|
|
7
7
|
import nodemon from 'nodemon';
|
|
8
|
+
import dotenv from 'dotenv';
|
|
8
9
|
import { shellExec } from './process.js';
|
|
9
10
|
import { loggerFactory } from './logger.js';
|
|
11
|
+
import Underpost from '../index.js';
|
|
10
12
|
|
|
11
13
|
const logger = loggerFactory(import.meta);
|
|
12
14
|
|
|
@@ -21,22 +23,20 @@ const logger = loggerFactory(import.meta);
|
|
|
21
23
|
* @returns {void}
|
|
22
24
|
* @memberof clientDevServer
|
|
23
25
|
*/
|
|
24
|
-
const createClientDevServer = (
|
|
26
|
+
const createClientDevServer = async (
|
|
25
27
|
deployId = process.argv[2] || 'dd-default',
|
|
26
28
|
subConf = process.argv[3] || '',
|
|
27
29
|
host = process.argv[4] || 'default.net',
|
|
28
30
|
path = process.argv[5] || '/',
|
|
29
31
|
) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
);
|
|
32
|
+
const devClientEnvPath = `./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-client`;
|
|
33
|
+
if (fs.existsSync(devClientEnvPath)) dotenv.config({ path: devClientEnvPath, override: true });
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
);
|
|
35
|
+
await Underpost.repo.client(deployId, `${subConf}-dev-client`.trim(), host, path);
|
|
36
|
+
|
|
37
|
+
shellExec(`node src/server ${deployId} ${subConf}-dev-client`.trim(), {
|
|
38
|
+
async: true,
|
|
39
|
+
});
|
|
40
40
|
|
|
41
41
|
// https://github.com/remy/nodemon/blob/main/doc/events.md
|
|
42
42
|
|
|
@@ -47,7 +47,7 @@ const createClientDevServer = (
|
|
|
47
47
|
// restart([ array of files triggering the restart ]) - child process has restarted
|
|
48
48
|
// config:update - nodemon's config has changed
|
|
49
49
|
|
|
50
|
-
if (fs.existsSync(
|
|
50
|
+
if (fs.existsSync(`/tmp/client.build.json`)) fs.removeSync(`/tmp/client.build.json`);
|
|
51
51
|
|
|
52
52
|
let buildPathScope = [];
|
|
53
53
|
|
|
@@ -77,10 +77,11 @@ const createClientDevServer = (
|
|
|
77
77
|
}, 2500);
|
|
78
78
|
const buildPathScopeBuild = buildPathScope.map((o) => o.path);
|
|
79
79
|
logger.info('buildPathScopeBuild', buildPathScopeBuild);
|
|
80
|
-
fs.writeFileSync(
|
|
80
|
+
fs.writeFileSync(`/tmp/client.build.json`, JSON.stringify(buildPathScopeBuild, null, 4));
|
|
81
81
|
})
|
|
82
82
|
.on('crash', function (error) {
|
|
83
|
-
logger.error(error, error.message);
|
|
83
|
+
if (error) logger.error(error, error.message || 'nodemon crash');
|
|
84
|
+
else logger.error('nodemon process crashed');
|
|
84
85
|
});
|
|
85
86
|
};
|
|
86
87
|
|