underpost 2.8.884 → 2.8.886
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 +3 -0
- package/.github/workflows/ghpkg.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +1 -1
- package/.github/workflows/publish.ci.yml +5 -5
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -1
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/CHANGELOG.md +145 -1
- package/Dockerfile +1 -1
- package/README.md +5 -121
- package/bin/build.js +18 -9
- package/bin/deploy.js +102 -197
- package/bin/file.js +4 -6
- package/cli.md +16 -12
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +54 -54
- package/manifests/deployment/dd-test-development/proxy.yaml +4 -4
- package/manifests/lxd/underpost-setup.sh +5 -5
- package/package.json +3 -3
- package/scripts/ssl.sh +164 -0
- package/src/cli/baremetal.js +7 -7
- package/src/cli/cloud-init.js +1 -1
- package/src/cli/cluster.js +31 -3
- package/src/cli/cron.js +9 -1
- package/src/cli/db.js +64 -2
- package/src/cli/deploy.js +189 -4
- package/src/cli/env.js +43 -0
- package/src/cli/fs.js +96 -2
- package/src/cli/image.js +15 -0
- package/src/cli/index.js +17 -4
- package/src/cli/monitor.js +33 -2
- package/src/cli/repository.js +95 -2
- package/src/cli/run.js +315 -51
- package/src/cli/script.js +32 -0
- package/src/cli/secrets.js +34 -0
- package/src/cli/test.js +42 -1
- package/src/client/components/core/Css.js +16 -8
- package/src/client/components/core/Docs.js +5 -13
- package/src/client/components/core/Modal.js +48 -29
- package/src/client/components/core/Router.js +6 -3
- package/src/client/components/core/Worker.js +205 -118
- package/src/client/components/core/windowGetDimensions.js +229 -162
- package/src/client/components/default/MenuDefault.js +1 -0
- package/src/client.dev.js +6 -3
- package/src/db/DataBaseProvider.js +65 -12
- package/src/db/mariadb/MariaDB.js +39 -6
- package/src/db/mongo/MongooseDB.js +51 -133
- package/src/index.js +2 -2
- package/src/mailer/EmailRender.js +58 -9
- package/src/mailer/MailerProvider.js +99 -25
- package/src/runtime/express/Express.js +32 -38
- package/src/runtime/lampp/Dockerfile +1 -1
- package/src/server/auth.js +9 -28
- package/src/server/backup.js +20 -0
- package/src/server/client-build-live.js +23 -12
- package/src/server/client-build.js +136 -91
- package/src/server/client-dev-server.js +35 -8
- package/src/server/client-icons.js +19 -0
- package/src/server/conf.js +543 -80
- package/src/server/dns.js +184 -42
- package/src/server/downloader.js +65 -24
- package/src/server/object-layer.js +260 -162
- package/src/server/peer.js +3 -9
- package/src/server/proxy.js +93 -76
- package/src/server/runtime.js +15 -21
- package/src/server/ssr.js +4 -4
- package/src/server/start.js +39 -0
- package/src/server/tls.js +251 -0
- package/src/server/valkey.js +11 -10
- package/src/ws/IoInterface.js +133 -39
- package/src/ws/IoServer.js +80 -31
- package/src/ws/core/core.ws.connection.js +50 -16
- package/src/ws/core/core.ws.emit.js +47 -8
- package/src/ws/core/core.ws.server.js +62 -10
- package/manifests/maas/lxd-preseed.yaml +0 -32
- package/src/server/ssl.js +0 -108
- /package/{manifests/maas → scripts}/device-scan.sh +0 -0
- /package/{manifests/maas → scripts}/gpu-diag.sh +0 -0
- /package/{manifests/maas → scripts}/maas-setup.sh +0 -0
- /package/{manifests/maas → scripts}/nat-iptables.sh +0 -0
- /package/{manifests/maas → scripts}/nvim.sh +0 -0
- /package/{manifests/maas → scripts}/snap-clean.sh +0 -0
- /package/{manifests/maas → scripts}/ssh-cluster-info.sh +0 -0
package/src/server/auth.js
CHANGED
|
@@ -325,33 +325,13 @@ const validatePasswordMiddleware = (req) => {
|
|
|
325
325
|
/**
|
|
326
326
|
* Creates cookie options for the refresh token.
|
|
327
327
|
* @param {import('express').Request} req The Express request object.
|
|
328
|
+
* @param {string} host The host name.
|
|
328
329
|
* @returns {object} Cookie options.
|
|
329
330
|
* @memberof Auth
|
|
330
331
|
*/
|
|
331
|
-
const cookieOptionsFactory = (req) => {
|
|
332
|
+
const cookieOptionsFactory = (req, host) => {
|
|
332
333
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
333
334
|
|
|
334
|
-
// Determine hostname safely:
|
|
335
|
-
// Prefer origin header if present (it contains protocol + host)
|
|
336
|
-
let candidateHost = undefined;
|
|
337
|
-
try {
|
|
338
|
-
if (req.headers && req.headers.origin) {
|
|
339
|
-
candidateHost = new URL(req.headers.origin).hostname;
|
|
340
|
-
}
|
|
341
|
-
} catch (e) {
|
|
342
|
-
/* ignore parse error */
|
|
343
|
-
logger.error(e);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// fallback to req.hostname (Express sets this; ensure trust proxy if behind proxy)
|
|
347
|
-
if (!candidateHost) candidateHost = (req.hostname || '').split(':')[0];
|
|
348
|
-
|
|
349
|
-
candidateHost = (candidateHost || '').trim().replace(/^www\./i, '');
|
|
350
|
-
|
|
351
|
-
// Do not set domain for localhost, 127.x.x.x, or plain IPs
|
|
352
|
-
const isIpOrLocal = /^(localhost|127(?:\.\d+){0,2}\.\d+|\[::1\]|\d+\.\d+\.\d+\.\d+)$/i.test(candidateHost);
|
|
353
|
-
const domain = isProduction && candidateHost && !isIpOrLocal ? `.${candidateHost}` : undefined;
|
|
354
|
-
|
|
355
335
|
// Determine if request is secure: respect X-Forwarded-Proto when behind proxy
|
|
356
336
|
const forwardedProto = (req.headers && req.headers['x-forwarded-proto']) || '';
|
|
357
337
|
const reqIsSecure = Boolean(req.secure || forwardedProto.split(',')[0] === 'https');
|
|
@@ -361,17 +341,16 @@ const cookieOptionsFactory = (req) => {
|
|
|
361
341
|
const sameSite = secure ? 'None' : 'Lax';
|
|
362
342
|
|
|
363
343
|
// Safe parse of maxAge minutes
|
|
364
|
-
const
|
|
365
|
-
const maxAge = Number.isFinite(minutes) && minutes > 0 ? minutes * 60 * 1000 : undefined;
|
|
344
|
+
const maxAge = parseInt(process.env.ACCESS_EXPIRE_MINUTES) * 60 * 1000;
|
|
366
345
|
|
|
367
346
|
const opts = {
|
|
368
347
|
httpOnly: true,
|
|
369
348
|
secure,
|
|
370
349
|
sameSite,
|
|
371
350
|
path: '/',
|
|
351
|
+
domain: process.env.NODE_ENV === 'production' ? host : 'localhost',
|
|
352
|
+
maxAge,
|
|
372
353
|
};
|
|
373
|
-
if (typeof maxAge !== 'undefined') opts.maxAge = maxAge;
|
|
374
|
-
if (domain) opts.domain = domain;
|
|
375
354
|
|
|
376
355
|
return opts;
|
|
377
356
|
};
|
|
@@ -409,7 +388,7 @@ async function createSessionAndUserToken(user, User, req, res, options = { host:
|
|
|
409
388
|
const jwtid = session._id.toString();
|
|
410
389
|
|
|
411
390
|
// Secure cookie settings
|
|
412
|
-
res.cookie('refreshToken', refreshToken, cookieOptionsFactory(req));
|
|
391
|
+
res.cookie('refreshToken', refreshToken, cookieOptionsFactory(req, options.host));
|
|
413
392
|
|
|
414
393
|
return { jwtid };
|
|
415
394
|
}
|
|
@@ -512,6 +491,7 @@ async function refreshSessionAndToken(req, res, User, options = { host: '', path
|
|
|
512
491
|
|
|
513
492
|
if (!user) {
|
|
514
493
|
// Possible token reuse: look up user by some other signals? If not possible, log and throw.
|
|
494
|
+
// TODO: on cors requests, this will throw an error, because the cookie is not sent.
|
|
515
495
|
logger.warn('Refresh token reuse or invalid token detected');
|
|
516
496
|
// Optional: revoke by clearing cookie and returning unauthorized
|
|
517
497
|
res.clearCookie('refreshToken', { path: '/' });
|
|
@@ -543,7 +523,7 @@ async function refreshSessionAndToken(req, res, User, options = { host: '', path
|
|
|
543
523
|
|
|
544
524
|
logger.warn('Refreshed session for user ' + user.email);
|
|
545
525
|
|
|
546
|
-
res.cookie('refreshToken', refreshToken, cookieOptionsFactory(req));
|
|
526
|
+
res.cookie('refreshToken', refreshToken, cookieOptionsFactory(req, options.host));
|
|
547
527
|
|
|
548
528
|
return jwtSign(
|
|
549
529
|
UserDto.auth.payload(user, session._id.toString(), req.ip, req.headers['user-agent'], options.host, options.path),
|
|
@@ -663,6 +643,7 @@ function applySecurity(app, opts = {}) {
|
|
|
663
643
|
maxAge: 600,
|
|
664
644
|
}),
|
|
665
645
|
);
|
|
646
|
+
logger.info('Cors origin', origin);
|
|
666
647
|
|
|
667
648
|
// Rate limiting + slow down
|
|
668
649
|
const limiter = rateLimit({
|
package/src/server/backup.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages backup operations for deployments.
|
|
3
|
+
* @module src/server/backup.js
|
|
4
|
+
* @namespace BackUp
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import fs from 'fs-extra';
|
|
2
8
|
import { loggerFactory } from './logger.js';
|
|
3
9
|
import { shellExec } from './process.js';
|
|
@@ -8,7 +14,21 @@ dotenv.config();
|
|
|
8
14
|
|
|
9
15
|
const logger = loggerFactory(import.meta);
|
|
10
16
|
|
|
17
|
+
/**
|
|
18
|
+
* @class BackUp
|
|
19
|
+
* @description Manages backup operations for deployments.
|
|
20
|
+
* @memberof BackUp
|
|
21
|
+
*/
|
|
11
22
|
class BackUp {
|
|
23
|
+
/**
|
|
24
|
+
* @method callback
|
|
25
|
+
* @description Initiates a backup operation for the specified deployment list.
|
|
26
|
+
* @param {string} deployList - The list of deployments to backup.
|
|
27
|
+
* @param {Object} options - The options for the backup operation.
|
|
28
|
+
* @param {boolean} options.itc - Whether to backup inside container data.
|
|
29
|
+
* @param {boolean} options.git - Whether to backup data using Git.
|
|
30
|
+
* @memberof BackUp
|
|
31
|
+
*/
|
|
12
32
|
static callback = async function (deployList, options = { itc: false, git: false }) {
|
|
13
33
|
if ((!deployList || deployList === 'dd') && fs.existsSync(`./engine-private/deploy/dd.router`))
|
|
14
34
|
deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module for live building client-side code
|
|
3
|
+
* @module src/server/client-build-live.js
|
|
4
|
+
* @namespace clientLiveBuild
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import fs from 'fs-extra';
|
|
2
8
|
import { Config, loadConf } from './conf.js';
|
|
3
9
|
import { loggerFactory } from './logger.js';
|
|
@@ -5,21 +11,28 @@ import { buildClient } from './client-build.js';
|
|
|
5
11
|
|
|
6
12
|
const logger = loggerFactory(import.meta);
|
|
7
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @function clientLiveBuild
|
|
16
|
+
* @description Initiates a live build of client-side code.
|
|
17
|
+
* @memberof clientLiveBuild
|
|
18
|
+
*/
|
|
8
19
|
const clientLiveBuild = async () => {
|
|
9
20
|
if (fs.existsSync(`./tmp/client.build.json`)) {
|
|
10
21
|
const deployId = process.argv[2];
|
|
11
|
-
|
|
22
|
+
const subConf = process.argv[3];
|
|
12
23
|
let clientId = 'default';
|
|
13
24
|
let host = 'default.net';
|
|
14
25
|
let path = '/';
|
|
15
26
|
let baseHost = `${host}${path === '/' ? '' : path}`;
|
|
16
27
|
let views = Config.default.client[clientId].views;
|
|
28
|
+
let apiBaseHost;
|
|
29
|
+
let apiBaseProxyPath;
|
|
17
30
|
|
|
18
31
|
if (
|
|
19
32
|
deployId &&
|
|
20
33
|
(fs.existsSync(`./engine-private/conf/${deployId}`) || fs.existsSync(`./engine-private/replica/${deployId}`))
|
|
21
34
|
) {
|
|
22
|
-
loadConf(deployId);
|
|
35
|
+
loadConf(deployId, subConf);
|
|
23
36
|
const confClient = JSON.parse(
|
|
24
37
|
fs.readFileSync(
|
|
25
38
|
fs.existsSync(`./engine-private/replica/${deployId}`)
|
|
@@ -31,29 +44,27 @@ const clientLiveBuild = async () => {
|
|
|
31
44
|
),
|
|
32
45
|
);
|
|
33
46
|
const confServer = JSON.parse(
|
|
34
|
-
fs.readFileSync(
|
|
35
|
-
fs.existsSync(`./engine-private/replica/${deployId}`)
|
|
36
|
-
? `./engine-private/replica/${deployId}/conf.server.json`
|
|
37
|
-
: fs.existsSync(`./engine-private/conf/${deployId}/conf.server.json`)
|
|
38
|
-
? `./engine-private/conf/${deployId}/conf.server.json`
|
|
39
|
-
: `./conf/conf.server.json`,
|
|
40
|
-
'utf8',
|
|
41
|
-
),
|
|
47
|
+
fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.dev.${subConf}.json`, 'utf8'),
|
|
42
48
|
);
|
|
43
|
-
host = process.argv[
|
|
44
|
-
path = process.argv[
|
|
49
|
+
host = process.argv[4];
|
|
50
|
+
path = process.argv[5];
|
|
45
51
|
clientId = confServer[host][path].client;
|
|
46
52
|
views = confClient[clientId].views;
|
|
47
53
|
baseHost = `${host}${path === '/' ? '' : path}`;
|
|
54
|
+
apiBaseHost = confServer[host][path].apiBaseHost;
|
|
55
|
+
apiBaseProxyPath = confServer[host][path].apiBaseProxyPath;
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
logger.info('Live build config', {
|
|
51
59
|
deployId,
|
|
60
|
+
subConf,
|
|
52
61
|
host,
|
|
53
62
|
path,
|
|
54
63
|
clientId,
|
|
55
64
|
baseHost,
|
|
56
65
|
views: views.length,
|
|
66
|
+
apiBaseHost,
|
|
67
|
+
apiBaseProxyPath,
|
|
57
68
|
});
|
|
58
69
|
|
|
59
70
|
const updates = JSON.parse(fs.readFileSync(`./tmp/client.build.json`, 'utf8'));
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages the client-side build process, including full builds and incremental builds.
|
|
3
|
+
* @module server/client-build.js
|
|
4
|
+
* @namespace clientBuild
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
'use strict';
|
|
2
8
|
|
|
3
9
|
import fs from 'fs-extra';
|
|
@@ -26,97 +32,17 @@ dotenv.config();
|
|
|
26
32
|
|
|
27
33
|
// Static Site Generation (SSG)
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
db,
|
|
41
|
-
dists,
|
|
42
|
-
rootClientPath,
|
|
43
|
-
acmeChallengeFullPath,
|
|
44
|
-
publicClientId,
|
|
45
|
-
iconsBuild,
|
|
46
|
-
metadata,
|
|
47
|
-
}) => {
|
|
48
|
-
logger.warn('Full build', rootClientPath);
|
|
49
|
-
|
|
50
|
-
buildAcmeChallengePath(acmeChallengeFullPath);
|
|
51
|
-
|
|
52
|
-
if (publicClientId && publicClientId.startsWith('html-website-templates')) {
|
|
53
|
-
if (!fs.existsSync(`/home/dd/html-website-templates/`))
|
|
54
|
-
shellExec(`cd /home/dd && git clone https://github.com/designmodo/html-website-templates.git`);
|
|
55
|
-
if (!fs.existsSync(`${rootClientPath}/index.php`)) {
|
|
56
|
-
fs.copySync(`/home/dd/html-website-templates/${publicClientId.split('-publicClientId-')[1]}`, rootClientPath);
|
|
57
|
-
shellExec(`cd ${rootClientPath} && git init && git add . && git commit -m "Base template implementation"`);
|
|
58
|
-
// git remote add origin git@github.com:<username>/<repo>.git
|
|
59
|
-
fs.writeFileSync(`${rootClientPath}/.git/.htaccess`, `Deny from all`, 'utf8');
|
|
60
|
-
}
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
fs.removeSync(rootClientPath);
|
|
65
|
-
|
|
66
|
-
if (fs.existsSync(`./src/client/public/${publicClientId}`)) {
|
|
67
|
-
if (iconsBuild === true) await buildIcons({ publicClientId, metadata });
|
|
68
|
-
|
|
69
|
-
fs.copySync(
|
|
70
|
-
`./src/client/public/${publicClientId}`,
|
|
71
|
-
rootClientPath /* {
|
|
72
|
-
filter: function (name) {
|
|
73
|
-
console.log(name);
|
|
74
|
-
return true;
|
|
75
|
-
},
|
|
76
|
-
} */,
|
|
77
|
-
);
|
|
78
|
-
} else if (fs.existsSync(`./engine-private/src/client/public/${publicClientId}`)) {
|
|
79
|
-
switch (publicClientId) {
|
|
80
|
-
case 'mysql_test':
|
|
81
|
-
if (db) {
|
|
82
|
-
fs.copySync(`./engine-private/src/client/public/${publicClientId}`, rootClientPath);
|
|
83
|
-
fs.writeFileSync(
|
|
84
|
-
`${rootClientPath}/index.php`,
|
|
85
|
-
fs
|
|
86
|
-
.readFileSync(`${rootClientPath}/index.php`, 'utf8')
|
|
87
|
-
.replace('test_servername', 'localhost')
|
|
88
|
-
.replace('test_username', db.user)
|
|
89
|
-
.replace('test_password', db.password)
|
|
90
|
-
.replace('test_dbname', db.name),
|
|
91
|
-
'utf8',
|
|
92
|
-
);
|
|
93
|
-
} else logger.error('not provided db config');
|
|
94
|
-
break;
|
|
95
|
-
|
|
96
|
-
default:
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
if (dists)
|
|
101
|
-
for (const dist of dists) {
|
|
102
|
-
if ('folder' in dist) {
|
|
103
|
-
if (fs.statSync(dist.folder).isDirectory()) {
|
|
104
|
-
fs.mkdirSync(`${rootClientPath}${dist.public_folder}`, { recursive: true });
|
|
105
|
-
fs.copySync(dist.folder, `${rootClientPath}${dist.public_folder}`);
|
|
106
|
-
} else {
|
|
107
|
-
const folder = dist.public_folder.split('/');
|
|
108
|
-
folder.pop();
|
|
109
|
-
fs.mkdirSync(`${rootClientPath}${folder.join('/')}`, { recursive: true });
|
|
110
|
-
fs.copyFileSync(dist.folder, `${rootClientPath}${dist.public_folder}`);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
if ('styles' in dist) {
|
|
114
|
-
fs.mkdirSync(`${rootClientPath}${dist.public_styles_folder}`, { recursive: true });
|
|
115
|
-
fs.copySync(dist.styles, `${rootClientPath}${dist.public_styles_folder}`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
35
|
+
/**
|
|
36
|
+
* @async
|
|
37
|
+
* @function buildClient
|
|
38
|
+
* @memberof clientBuild
|
|
39
|
+
* @param {Object} options - Options for the build process.
|
|
40
|
+
* @param {Array} options.liveClientBuildPaths - List of paths to build incrementally.
|
|
41
|
+
* @param {Array} options.instances - List of instances to build.
|
|
42
|
+
* @returns {Promise<void>} - Promise that resolves when the build is complete.
|
|
43
|
+
* @throws {Error} - If the build fails.
|
|
44
|
+
* @memberof clientBuild
|
|
45
|
+
*/
|
|
120
46
|
const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }) => {
|
|
121
47
|
const logger = loggerFactory(import.meta);
|
|
122
48
|
const confClient = JSON.parse(fs.readFileSync(`./conf/conf.client.json`, 'utf8'));
|
|
@@ -126,6 +52,125 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
126
52
|
const acmeChallengePath = `/.well-known/acme-challenge`;
|
|
127
53
|
const publicPath = `./public`;
|
|
128
54
|
|
|
55
|
+
/**
|
|
56
|
+
* @async
|
|
57
|
+
* @function buildAcmeChallengePath
|
|
58
|
+
* @memberof clientBuild
|
|
59
|
+
* @param {string} acmeChallengeFullPath - Full path to the acme-challenge directory.
|
|
60
|
+
* @returns {void}
|
|
61
|
+
* @throws {Error} - If the directory cannot be created.
|
|
62
|
+
* @memberof clientBuild
|
|
63
|
+
*/
|
|
64
|
+
const buildAcmeChallengePath = (acmeChallengeFullPath = '') => {
|
|
65
|
+
fs.mkdirSync(acmeChallengeFullPath, {
|
|
66
|
+
recursive: true,
|
|
67
|
+
});
|
|
68
|
+
fs.writeFileSync(`${acmeChallengeFullPath}/.gitkeep`, '', 'utf8');
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @async
|
|
73
|
+
* @function fullBuild
|
|
74
|
+
* @memberof clientBuild
|
|
75
|
+
* @param {Object} options - Options for the full build process.
|
|
76
|
+
* @param {string} options.path - Path to the client directory.
|
|
77
|
+
* @param {Object} options.logger - Logger instance.
|
|
78
|
+
* @param {string} options.client - Client name.
|
|
79
|
+
* @param {Object} options.db - Database configuration.
|
|
80
|
+
* @param {Array} options.dists - List of distributions to build.
|
|
81
|
+
* @param {string} options.rootClientPath - Full path to the client directory.
|
|
82
|
+
* @param {string} options.acmeChallengeFullPath - Full path to the acme-challenge directory.
|
|
83
|
+
* @param {string} options.publicClientId - Public client ID.
|
|
84
|
+
* @param {boolean} options.iconsBuild - Whether to build icons.
|
|
85
|
+
* @param {Object} options.metadata - Metadata for the client.
|
|
86
|
+
* @returns {Promise<void>} - Promise that resolves when the full build is complete.
|
|
87
|
+
* @throws {Error} - If the full build fails.
|
|
88
|
+
* @memberof clientBuild
|
|
89
|
+
*/
|
|
90
|
+
const fullBuild = async ({
|
|
91
|
+
path,
|
|
92
|
+
logger,
|
|
93
|
+
client,
|
|
94
|
+
db,
|
|
95
|
+
dists,
|
|
96
|
+
rootClientPath,
|
|
97
|
+
acmeChallengeFullPath,
|
|
98
|
+
publicClientId,
|
|
99
|
+
iconsBuild,
|
|
100
|
+
metadata,
|
|
101
|
+
}) => {
|
|
102
|
+
logger.warn('Full build', rootClientPath);
|
|
103
|
+
|
|
104
|
+
buildAcmeChallengePath(acmeChallengeFullPath);
|
|
105
|
+
|
|
106
|
+
if (publicClientId && publicClientId.startsWith('html-website-templates')) {
|
|
107
|
+
if (!fs.existsSync(`/home/dd/html-website-templates/`))
|
|
108
|
+
shellExec(`cd /home/dd && git clone https://github.com/designmodo/html-website-templates.git`);
|
|
109
|
+
if (!fs.existsSync(`${rootClientPath}/index.php`)) {
|
|
110
|
+
fs.copySync(`/home/dd/html-website-templates/${publicClientId.split('-publicClientId-')[1]}`, rootClientPath);
|
|
111
|
+
shellExec(`cd ${rootClientPath} && git init && git add . && git commit -m "Base template implementation"`);
|
|
112
|
+
// git remote add origin git@github.com:<username>/<repo>.git
|
|
113
|
+
fs.writeFileSync(`${rootClientPath}/.git/.htaccess`, `Deny from all`, 'utf8');
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
fs.removeSync(rootClientPath);
|
|
119
|
+
|
|
120
|
+
if (fs.existsSync(`./src/client/public/${publicClientId}`)) {
|
|
121
|
+
if (iconsBuild === true) await buildIcons({ publicClientId, metadata });
|
|
122
|
+
|
|
123
|
+
fs.copySync(
|
|
124
|
+
`./src/client/public/${publicClientId}`,
|
|
125
|
+
rootClientPath /* {
|
|
126
|
+
filter: function (name) {
|
|
127
|
+
console.log(name);
|
|
128
|
+
return true;
|
|
129
|
+
},
|
|
130
|
+
} */,
|
|
131
|
+
);
|
|
132
|
+
} else if (fs.existsSync(`./engine-private/src/client/public/${publicClientId}`)) {
|
|
133
|
+
switch (publicClientId) {
|
|
134
|
+
case 'mysql_test':
|
|
135
|
+
if (db) {
|
|
136
|
+
fs.copySync(`./engine-private/src/client/public/${publicClientId}`, rootClientPath);
|
|
137
|
+
fs.writeFileSync(
|
|
138
|
+
`${rootClientPath}/index.php`,
|
|
139
|
+
fs
|
|
140
|
+
.readFileSync(`${rootClientPath}/index.php`, 'utf8')
|
|
141
|
+
.replace('test_servername', 'localhost')
|
|
142
|
+
.replace('test_username', db.user)
|
|
143
|
+
.replace('test_password', db.password)
|
|
144
|
+
.replace('test_dbname', db.name),
|
|
145
|
+
'utf8',
|
|
146
|
+
);
|
|
147
|
+
} else logger.error('not provided db config');
|
|
148
|
+
break;
|
|
149
|
+
|
|
150
|
+
default:
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (dists)
|
|
155
|
+
for (const dist of dists) {
|
|
156
|
+
if ('folder' in dist) {
|
|
157
|
+
if (fs.statSync(dist.folder).isDirectory()) {
|
|
158
|
+
fs.mkdirSync(`${rootClientPath}${dist.public_folder}`, { recursive: true });
|
|
159
|
+
fs.copySync(dist.folder, `${rootClientPath}${dist.public_folder}`);
|
|
160
|
+
} else {
|
|
161
|
+
const folder = dist.public_folder.split('/');
|
|
162
|
+
folder.pop();
|
|
163
|
+
fs.mkdirSync(`${rootClientPath}${folder.join('/')}`, { recursive: true });
|
|
164
|
+
fs.copyFileSync(dist.folder, `${rootClientPath}${dist.public_folder}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if ('styles' in dist) {
|
|
168
|
+
fs.mkdirSync(`${rootClientPath}${dist.public_styles_folder}`, { recursive: true });
|
|
169
|
+
fs.copySync(dist.styles, `${rootClientPath}${dist.public_styles_folder}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
129
174
|
// { srcBuildPath, publicBuildPath }
|
|
130
175
|
const enableLiveRebuild =
|
|
131
176
|
options && options.liveClientBuildPaths && options.liveClientBuildPaths.length > 0 ? true : false;
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module for creating a client-side development server
|
|
3
|
+
* @module src/server/client-dev-server.js
|
|
4
|
+
* @namespace clientDevServer
|
|
5
|
+
*/
|
|
1
6
|
import fs from 'fs-extra';
|
|
2
7
|
import nodemon from 'nodemon';
|
|
3
8
|
import { shellExec } from './process.js';
|
|
@@ -5,14 +10,32 @@ import { loggerFactory } from './logger.js';
|
|
|
5
10
|
|
|
6
11
|
const logger = loggerFactory(import.meta);
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
/**
|
|
14
|
+
* @function createClientDevServer
|
|
15
|
+
* @description Creates a client-side development server.
|
|
16
|
+
* @memberof clientDevServer
|
|
17
|
+
* @param {string} deployId - The deployment ID.
|
|
18
|
+
* @param {string} subConf - The sub-configuration.
|
|
19
|
+
* @param {string} host - The host.
|
|
20
|
+
* @param {string} path - The path.
|
|
21
|
+
* @returns {void}
|
|
22
|
+
* @memberof clientDevServer
|
|
23
|
+
*/
|
|
24
|
+
const createClientDevServer = (
|
|
25
|
+
deployId = process.argv[2] || 'dd-default',
|
|
26
|
+
subConf = process.argv[3] || '',
|
|
27
|
+
host = process.argv[4] || 'default.net',
|
|
28
|
+
path = process.argv[5] || '/',
|
|
29
|
+
) => {
|
|
11
30
|
shellExec(
|
|
12
|
-
`env-cmd -f .env.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
31
|
+
`env-cmd -f ./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-client node bin/deploy build-full-client ${deployId} ${subConf}-dev-client ${host} ${path}`.trim(),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
shellExec(
|
|
35
|
+
`env-cmd -f ./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-client node src/server ${deployId} ${subConf}-dev-client`.trim(),
|
|
36
|
+
{
|
|
37
|
+
async: true,
|
|
38
|
+
},
|
|
16
39
|
);
|
|
17
40
|
|
|
18
41
|
// https://github.com/remy/nodemon/blob/main/doc/events.md
|
|
@@ -28,7 +51,11 @@ const createClientDevServer = () => {
|
|
|
28
51
|
|
|
29
52
|
let buildPathScope = [];
|
|
30
53
|
|
|
31
|
-
const nodemonOptions = {
|
|
54
|
+
const nodemonOptions = {
|
|
55
|
+
script: './src/client.build',
|
|
56
|
+
args: [`${deployId}`, `${subConf}-dev-client`, `${host}`, `${path}`],
|
|
57
|
+
watch: 'src/client',
|
|
58
|
+
};
|
|
32
59
|
logger.info('nodemon option', { nodemonOptions });
|
|
33
60
|
nodemon(nodemonOptions)
|
|
34
61
|
.on('start', function (...args) {
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module for building client-side icons
|
|
3
|
+
* @module src/server/client-icons.js
|
|
4
|
+
* @namespace clientIcons
|
|
5
|
+
*/
|
|
1
6
|
import { favicons } from 'favicons';
|
|
2
7
|
import { loggerFactory } from './logger.js';
|
|
3
8
|
import fs from 'fs-extra';
|
|
@@ -5,6 +10,20 @@ import { getCapVariableName } from '../client/components/core/CommonJs.js';
|
|
|
5
10
|
|
|
6
11
|
const logger = loggerFactory(import.meta);
|
|
7
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @function buildIcons
|
|
15
|
+
* @description Builds icons for a client-side application.
|
|
16
|
+
* @memberof clientIcons
|
|
17
|
+
* @param {Object} metadata - The metadata for the client-side application.
|
|
18
|
+
* @param {string} metadata.title - The title of the client-side application.
|
|
19
|
+
* @param {string} metadata.description - The description of the client-side application.
|
|
20
|
+
* @param {string} metadata.keywords - The keywords for the client-side application.
|
|
21
|
+
* @param {string} metadata.author - The author of the client-side application.
|
|
22
|
+
* @param {string} metadata.thumbnail - The thumbnail of the client-side application.
|
|
23
|
+
* @param {string} metadata.themeColor - The theme color of the client-side application.
|
|
24
|
+
* @param {string} metadata.baseBuildIconReference - The base build icon reference for the client-side application.
|
|
25
|
+
* @returns {Promise<void>}
|
|
26
|
+
*/
|
|
8
27
|
const buildIcons = async ({
|
|
9
28
|
publicClientId,
|
|
10
29
|
metadata: { title, description, keywords, author, thumbnail, themeColor, baseBuildIconReference },
|