underpost 2.8.858 → 2.8.866

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 CHANGED
@@ -1,7 +1,12 @@
1
1
  DEPLOY_ID=dd-default
2
+ DEFAULT_DEPLOY_ID=dd-default
2
3
  NODE_ENV=development
3
4
  PORT=4000
4
5
  JWT_SECRET=test
5
6
  EXPIRE=168
6
7
  NODE_OPTIONS=--max-old-space-size=8192
7
- BASE_API=api
8
+ BASE_API=api
9
+ DEFAULT_DEPLOY_HOST=default.net
10
+ DEFAULT_DEPLOY_PATH=/
11
+ DEFAULT_ADMIN_EMAIL=admin@default.net
12
+ DEFAULT_ADMIN_PASSWORD=changethis
package/.env.production CHANGED
@@ -1,7 +1,12 @@
1
1
  DEPLOY_ID=dd-default
2
+ DEFAULT_DEPLOY_ID=dd-default
2
3
  NODE_ENV=production
3
4
  PORT=3000
4
5
  JWT_SECRET=test
5
6
  EXPIRE=168
6
7
  NODE_OPTIONS=--max-old-space-size=8192
7
- BASE_API=api
8
+ BASE_API=api
9
+ DEFAULT_DEPLOY_HOST=default.net
10
+ DEFAULT_DEPLOY_PATH=/
11
+ DEFAULT_ADMIN_EMAIL=admin@default.net
12
+ DEFAULT_ADMIN_PASSWORD=changethis
package/.env.test CHANGED
@@ -1,7 +1,12 @@
1
1
  DEPLOY_ID=dd-default
2
+ DEFAULT_DEPLOY_ID=dd-default
2
3
  NODE_ENV=test
3
4
  PORT=5000
4
5
  JWT_SECRET=test
5
6
  EXPIRE=168
6
7
  NODE_OPTIONS=--max-old-space-size=8192
7
- BASE_API=api
8
+ BASE_API=api
9
+ DEFAULT_DEPLOY_HOST=default.net
10
+ DEFAULT_DEPLOY_PATH=/
11
+ DEFAULT_ADMIN_EMAIL=admin@default.net
12
+ DEFAULT_ADMIN_PASSWORD=changethis
package/README.md CHANGED
@@ -23,10 +23,16 @@
23
23
 
24
24
 
25
25
 
26
+
27
+
28
+
29
+
30
+
31
+
26
32
  <!-- badges -->
27
33
 
28
34
 
29
- [![Node.js CI](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/docker-image.yml) [![Test](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/coverall.yml) [![Downloads](https://img.shields.io/npm/dm/underpost.svg)](https://www.npmjs.com/package/underpost) [![Socket Badge](https://socket.dev/api/badge/npm/package/underpost/2.8.858)](https://socket.dev/npm/package/underpost/overview/2.8.858) [![Coverage Status](https://coveralls.io/repos/github/underpostnet/engine/badge.svg?branch=master)](https://coveralls.io/github/underpostnet/engine?branch=master) [![Version](https://img.shields.io/npm/v/underpost.svg)](https://www.npmjs.org/package/underpost) [![License](https://img.shields.io/npm/l/underpost.svg)](https://www.npmjs.com/package/underpost)
35
+ [![Node.js CI](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/docker-image.yml) [![Test](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/coverall.yml) [![Downloads](https://img.shields.io/npm/dm/underpost.svg)](https://www.npmjs.com/package/underpost) [![Socket Badge](https://socket.dev/api/badge/npm/package/underpost/2.8.866)](https://socket.dev/npm/package/underpost/overview/2.8.866) [![Coverage Status](https://coveralls.io/repos/github/underpostnet/engine/badge.svg?branch=master)](https://coveralls.io/github/underpostnet/engine?branch=master) [![Version](https://img.shields.io/npm/v/underpost.svg)](https://www.npmjs.org/package/underpost) [![License](https://img.shields.io/npm/l/underpost.svg)](https://www.npmjs.com/package/underpost)
30
36
 
31
37
 
32
38
  <!-- end-badges -->
@@ -38,6 +44,12 @@
38
44
 
39
45
 
40
46
 
47
+
48
+
49
+
50
+
51
+
52
+
41
53
  </div>
42
54
 
43
55
  <div align="center">
@@ -82,7 +94,7 @@ Run dev client server
82
94
  npm run dev
83
95
  ```
84
96
  <!-- -->
85
- ## underpost ci/cd cli v2.8.858
97
+ ## underpost ci/cd cli v2.8.866
86
98
 
87
99
  ### Usage: `underpost [options] [command]`
88
100
  ```
@@ -91,7 +103,7 @@ npm run dev
91
103
  -h, --help display help for command
92
104
 
93
105
  Commands:
94
- new <app-name> Initializes a new Underpost project with a predefined structure.
106
+ new [options] <app-name> Initializes a new Underpost project with a predefined structure.
95
107
  start [options] <deploy-id> [env] Initiates application servers, build pipelines, or other defined services based on the deployment ID.
96
108
  clone [options] <uri> Clones a specified GitHub repository into the current directory.
97
109
  pull [options] <path> <uri> Pulls the latest changes from a specified GitHub repository.
@@ -0,0 +1,78 @@
1
+ #! /usr/bin/env node
2
+
3
+ import dotenv from 'dotenv';
4
+ import { Command } from 'commander';
5
+ import fs from 'fs-extra';
6
+ import { pbcopy, shellExec } from '../src/server/process.js';
7
+ import Jimp from 'jimp';
8
+ import Underpost from '../src/index.js';
9
+ import { loggerFactory } from '../src/server/logger.js';
10
+ import { DataBaseProvider } from '../src/db/DataBaseProvider.js';
11
+
12
+ dotenv.config();
13
+
14
+ const logger = loggerFactory(import.meta);
15
+
16
+ const deployId = process.env.DEFAULT_DEPLOY_ID;
17
+ const host = process.env.DEFAULT_DEPLOY_HOST;
18
+ const path = process.env.DEFAULT_DEPLOY_PATH;
19
+
20
+ const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
21
+ const confServer = JSON.parse(fs.readFileSync(confServerPath, 'utf8'));
22
+ const { db } = confServer[host][path];
23
+
24
+ logger.info('env', {
25
+ deployId,
26
+ host,
27
+ path,
28
+ db,
29
+ });
30
+
31
+ await DataBaseProvider.load({
32
+ apis: ['object-layer'],
33
+ host,
34
+ path,
35
+ db,
36
+ });
37
+
38
+ const ObjectLayer = DataBaseProvider.instance[`${host}${path}`].mongoose.models.ObjectLayer;
39
+
40
+ const program = new Command();
41
+
42
+ program.name('cyberia').description(`content generator cli ${Underpost.version}`).version(Underpost.version);
43
+
44
+ const pngDirectoryIteratorByObjectLayerType = async (
45
+ objectLayerType = 'skin',
46
+ callback = ({ path, objectLayerType, objectLayerId, direction, frame }) => {},
47
+ ) => {
48
+ for (const objectLayerId of await fs.readdir(`./src/client/public/cyberia/assets/${objectLayerType}`)) {
49
+ for (const direction of await fs.readdir(
50
+ `./src/client/public/cyberia/assets/${objectLayerType}/${objectLayerId}`,
51
+ )) {
52
+ const dirFolder = `./src/client/public/cyberia/assets/${objectLayerType}/${objectLayerId}/${direction}`;
53
+ if (!fs.statSync(dirFolder).isDirectory()) continue;
54
+ for (const frame of await fs.readdir(dirFolder)) {
55
+ const imageFilePath = `./src/client/public/cyberia/assets/${objectLayerType}/${objectLayerId}/${direction}/${frame}`;
56
+ await callback({ path: imageFilePath, objectLayerType, objectLayerId, direction, frame });
57
+ }
58
+ }
59
+ }
60
+ };
61
+
62
+ program
63
+ .command('ol')
64
+ .option('--import [object-layer-type]', 'Import object layer from type storage png image')
65
+ .action(async (options = { import: false }) => {
66
+ if (options.import) {
67
+ await pngDirectoryIteratorByObjectLayerType(
68
+ options.import,
69
+ async ({ path, objectLayerType, objectLayerId, direction, frame }) => {
70
+ console.log(path, { objectLayerType, objectLayerId, direction, frame });
71
+ },
72
+ );
73
+ }
74
+ await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
75
+ })
76
+ .description('Object layer management');
77
+
78
+ program.parse();
package/bin/deploy.js CHANGED
@@ -39,19 +39,6 @@ const [exe, dir, operator] = process.argv;
39
39
 
40
40
  try {
41
41
  switch (operator) {
42
- case 'save':
43
- {
44
- let deployId = process.argv[3] ?? 'dd-default';
45
- if (!deployId.startsWith('dd-')) deployId = 'dd-' + deployId;
46
- const folder = `./engine-private/conf/${deployId}`;
47
- if (fs.existsSync(folder)) fs.removeSync(folder);
48
- await Config.build({ folder });
49
- fs.writeFileSync(`${folder}/.env.production`, fs.readFileSync('./.env.production', 'utf8'), 'utf8');
50
- fs.writeFileSync(`${folder}/.env.development`, fs.readFileSync('./.env.development', 'utf8'), 'utf8');
51
- fs.writeFileSync(`${folder}/.env.test`, fs.readFileSync('./.env.test', 'utf8'), 'utf8');
52
- fs.writeFileSync(`${folder}/package.json`, fs.readFileSync('./package.json', 'utf8'), 'utf8');
53
- }
54
- break;
55
42
  case 'add-nodejs-app-client-conf':
56
43
  {
57
44
  const toOptions = {
@@ -205,7 +192,7 @@ try {
205
192
  case 'build-full-client':
206
193
  {
207
194
  dotenv.config({ override: true });
208
- if (!process.argv[3]) process.argv[3] = 'default';
195
+ if (!process.argv[3]) process.argv[3] = 'dd-default';
209
196
  const { deployId, folder } = loadConf(process.argv[3]);
210
197
 
211
198
  let argHost = process.argv[4] ? process.argv[4].split(',') : [];
@@ -271,23 +258,20 @@ try {
271
258
  for (const deployIdObj of dataDeploy) {
272
259
  const { deployId, replicaHost } = deployIdObj;
273
260
  if (replicaHost && !singleReplicaHosts.includes(replicaHost)) singleReplicaHosts.push(replicaHost);
274
- const proxyInstance = deployId.match('proxy') || deployId.match('cron');
275
261
  const baseConfPath = fs.existsSync(`./engine-private/replica/${deployId}`)
276
262
  ? `./engine-private/replica`
277
263
  : `./engine-private/conf`;
278
264
  for (const envInstanceObj of dataEnv) {
279
265
  const envPath = `${baseConfPath}/${deployId}/.env.${envInstanceObj.env}`;
280
266
  const envObj = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
281
- envObj.PORT = proxyInstance
282
- ? envInstanceObj.port
283
- : envInstanceObj.port + port - singleReplicaHosts.length - (replicaHost ? 1 : 0);
267
+ envObj.PORT = envInstanceObj.port + port - singleReplicaHosts.length - (replicaHost ? 1 : 0);
284
268
 
285
269
  writeEnv(envPath, envObj);
286
270
  }
287
271
  const serverConf = loadReplicas(
288
272
  JSON.parse(fs.readFileSync(`${baseConfPath}/${deployId}/conf.server.json`, 'utf8')),
289
273
  );
290
- if (!proxyInstance) for (const host of Object.keys(serverConf)) port += Object.keys(serverConf[host]).length;
274
+ for (const host of Object.keys(serverConf)) port += Object.keys(serverConf[host]).length;
291
275
  }
292
276
  break;
293
277
 
@@ -387,6 +371,7 @@ try {
387
371
  shellCd(`/home/dd/engine`);
388
372
  const originPackageJson = JSON.parse(fs.readFileSync(`package.json`, 'utf8'));
389
373
  const newVersion = process.argv[3] ?? originPackageJson.version;
374
+ const node = process.argv[4] ?? 'kind-control-plane';
390
375
  const { version } = originPackageJson;
391
376
  originPackageJson.version = newVersion;
392
377
  fs.writeFileSync(`package.json`, JSON.stringify(originPackageJson, null, 4), 'utf8');
@@ -444,8 +429,10 @@ try {
444
429
  shellExec(`node bin/deploy update-dependencies`);
445
430
  shellExec(`auto-changelog`);
446
431
  shellExec(`node bin/build dd`);
447
- shellExec(`node bin deploy --kubeadm --build-manifest --sync --info-router --replicas 1 dd`);
448
- shellExec(`node bin deploy --kubeadm --build-manifest --sync --info-router --replicas 1 dd production`);
432
+ shellExec(`node bin deploy --kubeadm --build-manifest --sync --info-router --replicas 1 --node ${node} dd`);
433
+ shellExec(
434
+ `node bin deploy --kubeadm --build-manifest --sync --info-router --replicas 1 --node ${node} dd production`,
435
+ );
449
436
  break;
450
437
  }
451
438
 
package/bin/file.js CHANGED
@@ -125,8 +125,8 @@ try {
125
125
  templatePackageJson.scripts = originPackageJson.scripts;
126
126
  templatePackageJson.name = name;
127
127
  templatePackageJson.description = description;
128
- templatePackageJson.scripts.dev = dev;
129
- templatePackageJson.scripts.build = build;
128
+ // templatePackageJson.scripts.dev = dev;
129
+ // templatePackageJson.scripts.build = build;
130
130
  templatePackageJson.keywords = uniqueArray(
131
131
  ['pwa', 'microservices', 'template', 'builder'].concat(templatePackageJson.keywords),
132
132
  );
package/cli.md CHANGED
@@ -1,4 +1,4 @@
1
- ## underpost ci/cd cli v2.8.858
1
+ ## underpost ci/cd cli v2.8.866
2
2
 
3
3
  ### Usage: `underpost [options] [command]`
4
4
  ```
@@ -7,7 +7,7 @@
7
7
  -h, --help display help for command
8
8
 
9
9
  Commands:
10
- new <app-name> Initializes a new Underpost project with a predefined structure.
10
+ new [options] <app-name> Initializes a new Underpost project with a predefined structure.
11
11
  start [options] <deploy-id> [env] Initiates application servers, build pipelines, or other defined services based on the deployment ID.
12
12
  clone [options] <uri> Clones a specified GitHub repository into the current directory.
13
13
  pull [options] <path> <uri> Pulls the latest changes from a specified GitHub repository.
@@ -47,10 +47,12 @@ Commands:
47
47
  Initializes a new Underpost project with a predefined structure.
48
48
 
49
49
  Arguments:
50
- app-name The name of the application to create.
50
+ app-name The name or deploy-id of the application to create.
51
51
 
52
52
  Options:
53
- -h, --help display help for command
53
+ --deploy-id Crete deploy ID conf env files
54
+ --dev Sets the development cli context
55
+ -h, --help display help for command
54
56
 
55
57
  ```
56
58
 
@@ -289,6 +291,8 @@ Options:
289
291
  management commands.
290
292
  --cert Resets TLS/SSL certificate secrets for
291
293
  deployments.
294
+ --node <node> Sets optional node for deployment
295
+ operations.
292
296
  --build-manifest Builds Kubernetes YAML manifests, including
293
297
  deployments, services, proxies, and
294
298
  secrets.
@@ -593,7 +597,7 @@ Options:
593
597
  Runs a script from the specified path.
594
598
 
595
599
  Arguments:
596
- runner-id The runner ID to run. Options: spark-template, rmi, kill, secret, underpost-config, gpu-env, tf-gpu-test, dev-cluster, ssh-cluster-info, cyberia-ide, engine-ide, template-deploy, clean, pull, release-deploy, ssh-deploy, ide, dev-client, dev-api, monitor, db-client, cluster, deploy, tf-vae-test, deploy-job.
600
+ runner-id The runner ID to run. Options: spark-template, rmi, kill, secret, underpost-config, gpu-env, tf-gpu-test, dev-cluster, ssh-cluster-info, cyberia-ide, engine-ide, template-deploy, clean, pull, release-deploy, ssh-deploy, ide, dev-client, dev-api, router-sync, monitor, db-client, promote, metrics, cluster, deploy, tf-vae-test, deploy-job.
597
601
  path The absolute or relative directory path where the script is located.
598
602
 
599
603
  Options:
package/conf.js CHANGED
@@ -130,7 +130,7 @@ const DefaultConf = /**/ {
130
130
  ssr: {
131
131
  Default: {
132
132
  head: ['Seo', 'Pwa', 'Css', 'DefaultScripts', 'Production'],
133
- body: ['CacheControl', 'DefaultSplashScreen', '404', '500'],
133
+ body: ['CacheControl', 'DefaultSplashScreen'],
134
134
  mailer: { userVerifyEmail: 'DefaultVerifyEmail', userRecoverEmail: 'DefaultRecoverEmail' },
135
135
  offline: [
136
136
  { path: '/offline', title: 'No Network Connection', client: 'NoNetworkConnection', head: [], body: [] },
@@ -148,7 +148,7 @@ const DefaultConf = /**/ {
148
148
  origins: [],
149
149
  minifyBuild: false,
150
150
  iconsBuild: true,
151
- liteBuild: false,
151
+ liteBuild: true,
152
152
  docsBuild: false,
153
153
  offlineBuild: false,
154
154
  ws: 'core',
@@ -17,7 +17,7 @@ spec:
17
17
  spec:
18
18
  containers:
19
19
  - name: dd-default-development-blue
20
- image: localhost/rockylinux9-underpost:v2.8.858
20
+ image: localhost/rockylinux9-underpost:v2.8.866
21
21
  # resources:
22
22
  # requests:
23
23
  # memory: "124Ki"
@@ -100,7 +100,7 @@ spec:
100
100
  spec:
101
101
  containers:
102
102
  - name: dd-default-development-green
103
- image: localhost/rockylinux9-underpost:v2.8.858
103
+ image: localhost/rockylinux9-underpost:v2.8.866
104
104
  # resources:
105
105
  # requests:
106
106
  # memory: "124Ki"
package/package.json CHANGED
@@ -2,14 +2,14 @@
2
2
  "type": "module",
3
3
  "main": "src/index.js",
4
4
  "name": "underpost",
5
- "version": "2.8.858",
5
+ "version": "2.8.866",
6
6
  "description": "pwa api rest template",
7
7
  "scripts": {
8
8
  "start": "env-cmd -f .env.production node --max-old-space-size=8192 src/server",
9
9
  "build": "node bin/deploy build-full-client",
10
10
  "test": "env-cmd -f .env.test c8 mocha",
11
- "pm2": "env-cmd -f .env.production pm2 start src/server.js --node-args=\"--max-old-space-size=8192\" --name engine",
12
- "dev": "env-cmd -f .env.development node src/client.dev default",
11
+ "pm2": "env-cmd -f .env.production pm2 start src/server.js --node-args=\"--max-old-space-size=8192\" --name engine -- dd-default",
12
+ "dev": "env-cmd -f .env.development nodemon src/server",
13
13
  "dev-img": "env-cmd -f .env.development node src/server",
14
14
  "prod-img": "env-cmd -f .env.production node src/server",
15
15
  "monitor": "pm2 start src/monitor.js --name monitor -- dd production",
@@ -11,9 +11,9 @@ const UserRouter = (options) => {
11
11
  const router = express.Router();
12
12
 
13
13
  (async () => {
14
- const models = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models;
15
- if (models.User) {
16
- try {
14
+ try {
15
+ const models = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models;
16
+ if (models.User) {
17
17
  const adminUser = await models.User.findOne({ role: 'admin' });
18
18
  if (!adminUser) {
19
19
  const defaultPassword = process.env.DEFAULT_ADMIN_PASSWORD || 'changethis';
@@ -29,10 +29,12 @@ const UserRouter = (options) => {
29
29
  });
30
30
  logger.warn('Default admin user created. Please change the default password immediately!', result._doc);
31
31
  }
32
- } catch (error) {
33
- logger.error('Error checking/creating admin user:', error);
34
32
  }
33
+ } catch (error) {
34
+ logger.error('Error checking/creating admin user');
35
+ console.log(error);
35
36
  }
37
+
36
38
  options.png = {
37
39
  buffer: {
38
40
  'invalid-token': fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-invalid-token.png`),
@@ -683,17 +683,9 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`,
683
683
  }
684
684
  },
685
685
 
686
- /**
687
- * @method getResourcesCapacity
688
- * @description Retrieves and returns the allocatable CPU and memory resources
689
- * of the Kubernetes node.
690
- * @param {boolean} [isKubeadmOrK3s=false] - If true, assumes a kubeadm or k3s-managed node;
691
- * otherwise, assumes a Kind worker node.
692
- * @returns {object} An object containing CPU and memory resources with values and units.
693
- */
694
- getResourcesCapacity(isKubeadmOrK3s = false) {
686
+ getResourcesCapacity(node) {
695
687
  const resources = {};
696
- const nodeName = isKubeadmOrK3s ? os.hostname() : 'kind-worker';
688
+ const nodeName = node ?? os.hostname();
697
689
  const info = shellExec(`kubectl describe node ${nodeName} | grep -E '(Allocatable:|Capacity:)' -A 6`, {
698
690
  stdout: true,
699
691
  silent: true,
package/src/cli/deploy.js CHANGED
@@ -22,13 +22,13 @@ const logger = loggerFactory(import.meta);
22
22
  class UnderpostDeploy {
23
23
  static NETWORK = {};
24
24
  static API = {
25
- sync(deployList, { versions, replicas, kubeadm = false }) {
25
+ sync(deployList, { versions, replicas, node }) {
26
26
  const deployGroupId = 'dd.router';
27
27
  fs.writeFileSync(`./engine-private/deploy/${deployGroupId}`, deployList, 'utf8');
28
28
  const totalPods = deployList.split(',').length * versions.split(',').length * parseInt(replicas);
29
29
  const limitFactor = 0.8;
30
30
  const reserveFactor = 0.05;
31
- const resources = UnderpostCluster.API.getResourcesCapacity(kubeadm);
31
+ const resources = UnderpostCluster.API.getResourcesCapacity(node);
32
32
  const memory = parseInt(resources.memory.value / totalPods);
33
33
  const cpu = parseInt(resources.cpu.value / totalPods);
34
34
  UnderpostRootEnv.API.set(
@@ -49,7 +49,7 @@ class UnderpostDeploy {
49
49
  const initEnvObj = dotenv.parse(fs.readFileSync(initEnvPath, 'utf8'));
50
50
  process.env.PORT = initEnvObj.PORT;
51
51
  process.env.NODE_ENV = env;
52
- await Config.build(undefined, 'proxy', deployList);
52
+ await Config.build('proxy', deployList);
53
53
  return buildPortProxyRouter(env === 'development' ? 80 : 443, buildProxyRouter());
54
54
  },
55
55
  deploymentYamlServiceFactory({ deployId, env, port, deploymentVersions }) {
@@ -243,6 +243,7 @@ spec:
243
243
  versions: '',
244
244
  traffic: '',
245
245
  replicas: '',
246
+ node: '',
246
247
  restoreHosts: false,
247
248
  disableUpdateDeployment: false,
248
249
  infoTraffic: false,
@@ -480,6 +481,13 @@ Password: <Your Key>
480
481
  `kubectl create configmap underpost-config --from-file=/home/dd/engine/engine-private/conf/dd-cron/.env.${env}`,
481
482
  );
482
483
  },
484
+ switchTraffic(deployId, env, targetTraffic, replicas = 1) {
485
+ UnderpostRootEnv.API.set(`${deployId}-${env}-traffic`, targetTraffic);
486
+ shellExec(
487
+ `node bin deploy --info-router --build-manifest --traffic ${targetTraffic} --replicas ${replicas} ${deployId} ${env}`,
488
+ );
489
+ shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml`);
490
+ },
483
491
  };
484
492
  }
485
493
 
package/src/cli/index.js CHANGED
@@ -22,7 +22,9 @@ program.name('underpost').description(`underpost ci/cd cli ${Underpost.version}`
22
22
  // 'new' command: Create a new project
23
23
  program
24
24
  .command('new')
25
- .argument('<app-name>', 'The name of the application to create.')
25
+ .argument('<app-name>', 'The name or deploy-id of the application to create.')
26
+ .option('--deploy-id', 'Crete deploy ID conf env files')
27
+ .option('--dev', 'Sets the development cli context')
26
28
  .description('Initializes a new Underpost project with a predefined structure.')
27
29
  .action(Underpost.repo.new);
28
30
 
@@ -156,6 +158,7 @@ program
156
158
  .option('--expose', 'Exposes services matching the provided deployment ID list.')
157
159
  .option('--info-util', 'Displays useful `kubectl` utility management commands.')
158
160
  .option('--cert', 'Resets TLS/SSL certificate secrets for deployments.')
161
+ .option('--node <node>', 'Sets optional node for deployment operations.')
159
162
  .option(
160
163
  '--build-manifest',
161
164
  'Builds Kubernetes YAML manifests, including deployments, services, proxies, and secrets.',
@@ -5,6 +5,7 @@ import { actionInitLog, loggerFactory } from '../server/logger.js';
5
5
  import fs from 'fs-extra';
6
6
  import { getNpmRootPath } from '../server/conf.js';
7
7
  import UnderpostStartUp from '../server/start.js';
8
+ import { Config } from '../server/conf.js';
8
9
 
9
10
  dotenv.config();
10
11
 
@@ -80,24 +81,32 @@ class UnderpostRepository {
80
81
  );
81
82
  },
82
83
 
83
- new(repositoryName) {
84
+ new(repositoryName, options = { dev: false, deployId: false }) {
84
85
  return new Promise(async (resolve, reject) => {
85
86
  try {
86
87
  await logger.setUpInfo();
88
+ actionInitLog();
87
89
  if (repositoryName === 'service')
88
90
  return resolve(
89
91
  await UnderpostStartUp.API.listenPortController(UnderpostStartUp.API.listenServerFactory(), ':'),
90
92
  );
91
- else actionInitLog();
92
- const exeRootPath = `${getNpmRootPath()}/underpost`;
93
+ if (options.deployId === true) return Config.deployIdFactory(repositoryName);
94
+ const npmRoot = getNpmRootPath();
95
+ const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
93
96
  const destFolder = `./${repositoryName}`;
94
97
  logger.info('Note: This process may take several minutes to complete');
95
98
  logger.info('build app', { destFolder });
96
99
  if (fs.existsSync(destFolder)) fs.removeSync(destFolder);
97
100
  fs.mkdirSync(destFolder, { recursive: true });
98
- fs.copySync(exeRootPath, destFolder);
99
- fs.writeFileSync(`${destFolder}/.gitignore`, fs.readFileSync(`${exeRootPath}/.dockerignore`, 'utf8'), 'utf8');
100
- shellExec(`cd ${destFolder} && git init && git add . && git commit -m "Base template implementation"`);
101
+ if (!options.dev) {
102
+ fs.copySync(underpostRoot, destFolder);
103
+ fs.writeFileSync(
104
+ `${destFolder}/.gitignore`,
105
+ fs.readFileSync(`${underpostRoot}/.dockerignore`, 'utf8'),
106
+ 'utf8',
107
+ );
108
+ shellExec(`cd ${destFolder} && git init && git add . && git commit -m "Base template implementation"`);
109
+ }
101
110
  shellExec(`cd ${destFolder} && npm run build`);
102
111
  shellExec(`cd ${destFolder} && npm run dev`);
103
112
  return resolve();
package/src/cli/run.js CHANGED
@@ -141,6 +141,14 @@ class UnderpostRun {
141
141
  let [deployId, subConf] = path.split(',');
142
142
  shellExec(`npm run dev-api ${deployId} ${subConf}`);
143
143
  },
144
+ 'router-sync': (path, options = UnderpostRun.DEFAULT_OPTION) => {
145
+ const baseCommand = options.dev || true ? 'node bin' : 'underpost';
146
+ const defaultPaht = ['dd', 'kind-control-plane'];
147
+ let [deployId, node] = path ? path.split(',') : defaultPaht;
148
+ deployId = deployId ?? defaultPaht[0];
149
+ node = node ?? defaultPaht[1];
150
+ shellExec(`${baseCommand} deploy --sync --node ${node} --build-manifest --info-router ${deployId} production`);
151
+ },
144
152
  monitor: (path, options = UnderpostRun.DEFAULT_OPTION) => {
145
153
  const pid = getTerminalPid();
146
154
  logger.info('monitor pid', pid);
@@ -218,6 +226,34 @@ class UnderpostRun {
218
226
  const { underpostRoot } = options;
219
227
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/deployment/adminer/.`);
220
228
  },
229
+ promote: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
230
+ let [inputDeployId, inputEnv, inputReplicas] = path.split(',');
231
+ if (!inputEnv) inputEnv = 'production';
232
+ if (!inputReplicas) inputReplicas = 1;
233
+ if (inputDeployId === 'dd') {
234
+ for (const deployId of fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',')) {
235
+ const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId);
236
+ const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
237
+ UnderpostDeploy.API.switchTraffic(deployId, inputEnv, targetTraffic, inputReplicas);
238
+ }
239
+ } else {
240
+ const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(inputDeployId);
241
+ const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
242
+ UnderpostDeploy.API.switchTraffic(inputDeployId, inputEnv, targetTraffic, inputReplicas);
243
+ }
244
+ },
245
+
246
+ metrics: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
247
+ const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
248
+ let hosts = [];
249
+ for (const deployId of deployList) {
250
+ const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
251
+ hosts = hosts.concat(Object.keys(confServer));
252
+ }
253
+ shellExec(`node bin cluster --prom ${hosts.join(',')}`);
254
+ shellExec(`node bin cluster --grafana`);
255
+ },
256
+
221
257
  cluster: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
222
258
  const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
223
259
  const env = 'production';
@@ -268,14 +304,8 @@ class UnderpostRun {
268
304
 
269
305
  logger.info(`${iteratorTag} | Deployment ready. | Total delay number check iterations: ${checkStatusIteration}`);
270
306
 
271
- UnderpostRootEnv.API.set(`${deployId}-${env}-traffic`, targetTraffic);
307
+ UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic);
272
308
 
273
- shellExec(
274
- `node bin deploy --info-router --build-manifest --traffic ${targetTraffic} --replicas ${
275
- options.replicas ? options.replicas : 1
276
- } ${deployId} ${env}`,
277
- );
278
- shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml`);
279
309
  shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${currentTraffic}`);
280
310
  },
281
311
  'tf-vae-test': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
@@ -457,6 +457,19 @@ const getLang = () =>
457
457
  .slice(0, 2)
458
458
  .toLowerCase();
459
459
 
460
+ function hexToRgbA(hex) {
461
+ let c;
462
+ if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {
463
+ c = hex.substring(1).split('');
464
+ if (c.length == 3) {
465
+ c = [c[0], c[0], c[1], c[1], c[2], c[2]];
466
+ }
467
+ c = '0x' + c.join('');
468
+ return [(c >> 16) & 255, (c >> 8) & 255, c & 255];
469
+ }
470
+ throw new Error('Invalid Hex');
471
+ }
472
+
460
473
  export {
461
474
  s,
462
475
  htmls,
@@ -486,4 +499,5 @@ export {
486
499
  isDevInstance,
487
500
  getDataFromInputFile,
488
501
  getLang,
502
+ hexToRgbA,
489
503
  };
package/src/index.js CHANGED
@@ -35,7 +35,7 @@ class Underpost {
35
35
  * @type {String}
36
36
  * @memberof Underpost
37
37
  */
38
- static version = 'v2.8.858';
38
+ static version = 'v2.8.866';
39
39
  /**
40
40
  * Repository cli API
41
41
  * @static
@@ -159,7 +159,7 @@ const buildCoverage = async ({ host, path }) => {
159
159
  shellExec(`npm test`);
160
160
  }
161
161
 
162
- const coverageBuildPath = `${jsDocsConfig.opts.destination}/coverage`;
162
+ const coverageBuildPath = `${jsDocsConfig.opts.destination}coverage`;
163
163
  fs.mkdirSync(coverageBuildPath, { recursive: true });
164
164
  fs.copySync(`./coverage`, coverageBuildPath);
165
165
 
@@ -535,7 +535,7 @@ Sitemap: https://${host}${path === '/' ? '' : path}/sitemap.xml`,
535
535
  );
536
536
  }
537
537
 
538
- if (!enableLiveRebuild && !process.argv.includes('l') && !process.argv.includes('deploy') && docsBuild) {
538
+ if (fullBuildEnabled && !enableLiveRebuild && !process.argv.includes('l') && docsBuild) {
539
539
  await buildDocs({
540
540
  host,
541
541
  path,
@@ -27,68 +27,84 @@ dotenv.config();
27
27
 
28
28
  const logger = loggerFactory(import.meta);
29
29
 
30
- // monitoring: https://app.pm2.io/
31
-
32
30
  const Config = {
33
31
  default: DefaultConf,
34
- build: async function (options = { folder: '' }, deployContext, deployList, subConf) {
35
- if (!deployContext) deployContext = process.argv[2];
32
+ build: async function (deployContext = 'dd-default', deployList, subConf) {
33
+ if (typeof process.argv[2] === 'string' && process.argv[2].startsWith('dd-')) deployContext = process.argv[2];
36
34
  if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`, { recursive: true });
37
35
  fs.writeFileSync(`./tmp/await-deploy`, '', 'utf8');
38
- if (fs.existsSync(`./engine-private/conf/${deployContext}`))
39
- return loadConf(deployContext, process.env.NODE_ENV, subConf);
40
36
  if (fs.existsSync(`./engine-private/replica/${deployContext}`))
41
37
  return loadConf(deployContext, process.env.NODE_ENV, subConf);
38
+ else if (deployContext.startsWith('dd-')) return loadConf(deployContext, process.env.NODE_ENV, subConf);
39
+ if (deployContext === 'proxy') Config.buildProxy(deployContext, deployList, subConf);
40
+ },
41
+ deployIdFactory: function (deployId = 'dd-default') {
42
+ if (!deployId.startsWith('dd-')) deployId = `dd-${deployId}`;
42
43
 
43
- if (deployContext === 'deploy') return;
44
-
45
- if (deployContext === 'proxy') {
46
- if (!deployList) deployList = process.argv[3];
47
- if (!subConf) subConf = process.argv[4];
48
- this.default.server = {};
49
- for (const deployId of deployList.split(',')) {
50
- let confPath = `./engine-private/conf/${deployId}/conf.server.json`;
51
- const privateConfDevPath = fs.existsSync(`./engine-private/replica/${deployId}/conf.server.json`)
52
- ? `./engine-private/replica/${deployId}/conf.server.json`
53
- : `./engine-private/conf/${deployId}/conf.server.dev.${subConf}.json`;
54
- const confDevPath = fs.existsSync(privateConfDevPath)
55
- ? privateConfDevPath
56
- : `./engine-private/conf/${deployId}/conf.server.dev.json`;
57
-
58
- if (process.env.NODE_ENV === 'development' && fs.existsSync(confDevPath)) confPath = confDevPath;
59
- const serverConf = JSON.parse(fs.readFileSync(confPath, 'utf8'));
60
-
61
- for (const host of Object.keys(loadReplicas(serverConf, deployContext, subConf))) {
62
- if (serverConf[host]['/'])
63
- this.default.server[host] = {
64
- ...this.default.server[host],
65
- ...serverConf[host],
66
- };
67
- else
68
- this.default.server[host] = {
69
- ...serverConf[host],
70
- ...this.default.server[host],
71
- };
72
- }
44
+ logger.info('Build deployId', deployId);
45
+
46
+ const folder = `./engine-private/conf/${deployId}`;
47
+
48
+ if (!fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true });
49
+ fs.writeFileSync(
50
+ `${folder}/.env.production`,
51
+ fs.readFileSync('./.env.production', 'utf8').replace('dd-default', deployId),
52
+ 'utf8',
53
+ );
54
+ fs.writeFileSync(
55
+ `${folder}/.env.development`,
56
+ fs.readFileSync('./.env.development', 'utf8').replace('dd-default', deployId),
57
+ 'utf8',
58
+ );
59
+ fs.writeFileSync(
60
+ `${folder}/.env.test`,
61
+ fs.readFileSync('./.env.test', 'utf8').replace('dd-default', deployId),
62
+ 'utf8',
63
+ );
64
+ fs.writeFileSync(`${folder}/package.json`, fs.readFileSync('./package.json', 'utf8'), 'utf8');
65
+
66
+ this.buildTmpConf(folder);
67
+
68
+ return { deployIdFolder: folder, deployId };
69
+ },
70
+ buildTmpConf: function (folder = './conf') {
71
+ for (const confType of Object.keys(this.default))
72
+ fs.writeFileSync(`${folder}/conf.${confType}.json`, JSON.stringify(this.default[confType], null, 4), 'utf8');
73
+ },
74
+ buildProxy: function (deployContext = 'dd-default', deployList, subConf) {
75
+ if (!deployList) deployList = process.argv[3];
76
+ if (!subConf) subConf = process.argv[4];
77
+ this.default.server = {};
78
+ for (const deployId of deployList.split(',')) {
79
+ let confPath = `./engine-private/conf/${deployId}/conf.server.json`;
80
+ const privateConfDevPath = fs.existsSync(`./engine-private/replica/${deployId}/conf.server.json`)
81
+ ? `./engine-private/replica/${deployId}/conf.server.json`
82
+ : `./engine-private/conf/${deployId}/conf.server.dev.${subConf}.json`;
83
+ const confDevPath = fs.existsSync(privateConfDevPath)
84
+ ? privateConfDevPath
85
+ : `./engine-private/conf/${deployId}/conf.server.dev.json`;
86
+
87
+ if (process.env.NODE_ENV === 'development' && fs.existsSync(confDevPath)) confPath = confDevPath;
88
+ const serverConf = JSON.parse(fs.readFileSync(confPath, 'utf8'));
89
+
90
+ for (const host of Object.keys(loadReplicas(serverConf, deployContext, subConf))) {
91
+ if (serverConf[host]['/'])
92
+ this.default.server[host] = {
93
+ ...this.default.server[host],
94
+ ...serverConf[host],
95
+ };
96
+ else
97
+ this.default.server[host] = {
98
+ ...serverConf[host],
99
+ ...this.default.server[host],
100
+ };
73
101
  }
74
102
  }
75
- if (!options || !options.folder)
76
- options = {
77
- ...options,
78
- folder: `./conf`,
79
- };
80
- if (!fs.existsSync(options.folder)) fs.mkdirSync(options.folder, { recursive: true });
81
- for (const confType of Object.keys(this.default)) {
82
- fs.writeFileSync(
83
- `${options.folder}/conf.${confType}.json`,
84
- JSON.stringify(this.default[confType], null, 4),
85
- 'utf8',
86
- );
87
- }
103
+ this.buildTmpConf();
88
104
  },
89
105
  };
90
106
 
91
- const loadConf = (deployId, envInput, subConf) => {
107
+ const loadConf = (deployId = 'dd-default', envInput, subConf) => {
92
108
  if (deployId === 'current') {
93
109
  console.log(process.env.DEPLOY_ID);
94
110
  return;
@@ -107,18 +123,12 @@ const loadConf = (deployId, envInput, subConf) => {
107
123
  const folder = fs.existsSync(`./engine-private/replica/${deployId}`)
108
124
  ? `./engine-private/replica/${deployId}`
109
125
  : `./engine-private/conf/${deployId}`;
126
+ if (!fs.existsSync(folder)) Config.deployIdFactory(deployId);
110
127
  if (!fs.existsSync(`./conf`)) fs.mkdirSync(`./conf`);
111
- if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`, { recursive: true });
112
- const isValidDeployId = fs.existsSync(`${folder}`);
113
- if (!isValidDeployId) {
114
- logger.info(`Save new deploy conf: '${deployId}'`);
115
- shellExec(`node bin/deploy save ${deployId}`);
116
- return loadConf(deployId);
117
- }
128
+ if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`);
129
+
118
130
  for (const typeConf of Object.keys(Config.default)) {
119
- let srcConf = isValidDeployId
120
- ? fs.readFileSync(`${folder}/conf.${typeConf}.json`, 'utf8')
121
- : JSON.stringify(Config.default[typeConf]);
131
+ let srcConf = fs.readFileSync(`${folder}/conf.${typeConf}.json`, 'utf8');
122
132
  if (process.env.NODE_ENV === 'development' && typeConf === 'server') {
123
133
  if (!subConf) subConf = process.argv[3];
124
134
  const devConfPath = `${folder}/conf.${typeConf}.dev${subConf ? `.${subConf}` : ''}.json`;
@@ -493,15 +503,6 @@ const buildProxyRouter = () => {
493
503
  }
494
504
  }
495
505
  }
496
- if (process.argv.includes('maintenance'))
497
- (async () => {
498
- globalThis.defaultHtmlSrcMaintenance = (await ssrFactory())({
499
- title: 'Site in maintenance',
500
- ssrPath: '/',
501
- ssrHeadComponents: '',
502
- ssrBodyComponents: (await ssrFactory(`./src/client/ssr/offline/Maintenance.js`))(),
503
- });
504
- })();
505
506
 
506
507
  return proxyRouter;
507
508
  };
@@ -635,7 +636,6 @@ const getDataDeploy = (
635
636
  deployGroupId: '',
636
637
  deployId: '',
637
638
  disableSyncEnvPort: false,
638
- deployIdConcat: [],
639
639
  },
640
640
  ) => {
641
641
  let dataDeploy =
@@ -648,8 +648,6 @@ const getDataDeploy = (
648
648
  .map((deployId) => deployId.trim())
649
649
  .filter((deployId) => deployId);
650
650
 
651
- if (options.deployIdConcat) dataDeploy = dataDeploy.concat(options.deployIdConcat);
652
-
653
651
  if (options.deployId) dataDeploy = dataDeploy.filter((d) => d === options.deployId);
654
652
 
655
653
  dataDeploy = dataDeploy.map((deployId) => {
@@ -776,12 +774,6 @@ const awaitDeployMonitor = async (init = false, deltaMs = 1000) => {
776
774
  if (fs.existsSync(`./tmp/await-deploy`)) return await awaitDeployMonitor();
777
775
  };
778
776
 
779
- const getDeployId = () => {
780
- const deployIndexArg = process.argv.findIndex((a) => a.match(`deploy-id:`));
781
- if (deployIndexArg > -1) return process.argv[deployIndexArg].split(':')[1].trim();
782
- return 'dd-default';
783
- };
784
-
785
777
  const getCronBackUpFolder = (host = '', path = '') => {
786
778
  return `${host}${path.replace(/\\/g, '/').replace(`/`, '-')}`;
787
779
  };
@@ -998,7 +990,6 @@ export {
998
990
  buildReplicaId,
999
991
  getCronBackUpFolder,
1000
992
  mergeFile,
1001
- getDeployId,
1002
993
  getPathsSSR,
1003
994
  buildKindPorts,
1004
995
  buildPortProxyRouter,
@@ -55,7 +55,7 @@ const buildProxy = async () => {
55
55
  // '^/target-path': '/',
56
56
  },
57
57
  };
58
- if (!process.argv.includes('maintenance')) options.router = buildPortProxyRouter(port, proxyRouter);
58
+ options.router = buildPortProxyRouter(port, proxyRouter);
59
59
 
60
60
  const filter = false
61
61
  ? (pathname, req) => {
@@ -18,7 +18,6 @@ import { DataBaseProvider } from '../db/DataBaseProvider.js';
18
18
  // import { createProxyMiddleware } from 'http-proxy-middleware';
19
19
  import { createPeerServer } from './peer.js';
20
20
  import { Lampp } from '../runtime/lampp/Lampp.js';
21
- import { getDeployId } from './conf.js';
22
21
  import { JSONweb, ssrFactory } from './client-formatted.js';
23
22
  import Underpost from '../index.js';
24
23
  import { createValkeyConnection } from './valkey.js';
@@ -28,7 +27,7 @@ dotenv.config();
28
27
  const logger = loggerFactory(import.meta);
29
28
 
30
29
  const buildRuntime = async () => {
31
- const deployId = getDeployId();
30
+ const deployId = process.env.DEPLOY_ID;
32
31
 
33
32
  const collectDefaultMetrics = promClient.collectDefaultMetrics;
34
33
  collectDefaultMetrics();
@@ -79,11 +79,11 @@ class UnderpostStartUp {
79
79
  }
80
80
  }),
81
81
 
82
- async callback(deployId = 'default', env = 'development', options = { build: false, run: false }) {
82
+ async callback(deployId = 'dd-default', env = 'development', options = { build: false, run: false }) {
83
83
  if (options.build === true) await UnderpostStartUp.API.build(deployId, env);
84
84
  if (options.run === true) await UnderpostStartUp.API.run(deployId, env);
85
85
  },
86
- async build(deployId = 'default', env = 'development') {
86
+ async build(deployId = 'dd-default', env = 'development') {
87
87
  const buildBasePath = `/home/dd`;
88
88
  const repoName = `engine-${deployId.split('-')[1]}`;
89
89
  shellExec(`cd ${buildBasePath} && underpost clone underpostnet/${repoName}`);
@@ -100,19 +100,19 @@ class UnderpostStartUp {
100
100
  }
101
101
  shellExec(`node bin/deploy build-full-client ${deployId}`);
102
102
  },
103
- async run(deployId = 'default', env = 'development') {
103
+ async run(deployId = 'dd-default', env = 'development') {
104
104
  const runCmd = env === 'production' ? 'run prod-img' : 'run dev-img';
105
105
  if (fs.existsSync(`./engine-private/replica`)) {
106
106
  const replicas = await fs.readdir(`./engine-private/replica`);
107
107
  for (const replica of replicas) {
108
108
  if (!replica.match(deployId)) continue;
109
109
  shellExec(`node bin/deploy conf ${replica} ${env}`);
110
- shellExec(`npm ${runCmd} deploy deploy-id:${replica}`, { async: true });
110
+ shellExec(`npm ${runCmd} ${replica}`, { async: true });
111
111
  await awaitDeployMonitor(true);
112
112
  }
113
113
  }
114
114
  shellExec(`node bin/deploy conf ${deployId} ${env}`);
115
- shellExec(`npm ${runCmd} deploy deploy-id:${deployId}`, { async: true });
115
+ shellExec(`npm ${runCmd} ${deployId}`, { async: true });
116
116
  await awaitDeployMonitor(true);
117
117
  UnderpostRootEnv.API.set('container-status', `${deployId}-${env}-running-deployment`);
118
118
  },