underpost 2.8.635 → 2.8.637

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/Dockerfile CHANGED
@@ -1,11 +1,11 @@
1
1
  ARG BASE_DEBIAN=buster
2
2
 
3
+ # USER root
4
+
3
5
  FROM debian:${BASE_DEBIAN}
4
6
 
5
7
  ENV DEBIAN_FRONTEND=noninteractive
6
8
 
7
- WORKDIR /home/dd
8
-
9
9
  # Set root password to root, format is 'user:password'.
10
10
  RUN echo 'root:root' | chpasswd
11
11
 
@@ -25,9 +25,6 @@ RUN mkdir -p /var/run/sshd
25
25
  # Allow root login via password
26
26
  RUN sed -ri 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
27
27
 
28
- # copy supervisor config file to start openssh-server
29
- COPY supervisord-openssh-server.conf /etc/supervisor/conf.d/supervisord-openssh-server.conf
30
-
31
28
  # install open ssl git and others tools
32
29
  RUN apt-get install -yq --no-install-recommends libssl-dev curl wget git gnupg
33
30
 
@@ -37,12 +34,14 @@ RUN apt-get install -y nodejs build-essential
37
34
  RUN node --version
38
35
  RUN npm --version
39
36
 
40
- RUN npm install -g underpost
41
-
42
- VOLUME [ "/home/dd/engine/logs" ]
37
+ WORKDIR /home/dd
43
38
 
44
39
  EXPOSE 22
45
40
 
46
- EXPOSE 4000-4004
41
+ EXPOSE 80
42
+
43
+ EXPOSE 443
44
+
45
+ EXPOSE 3000-3100
47
46
 
48
- CMD [ "underpost", "new", "service" ]
47
+ EXPOSE 4000-4100
package/bin/build.js CHANGED
@@ -176,7 +176,7 @@ const { DefaultConf } = await import(`../conf.${confName}.js`);
176
176
  if (!fs.existsSync(`${basePath}/images`)) fs.mkdirSync(`${basePath}/images`);
177
177
 
178
178
  const env = process.argv.includes('development') ? 'development' : 'production';
179
- const deploymentsFiles = ['Dockerfile', 'proxy.yaml', 'deployment.yaml', 'secret.yaml'];
179
+ const deploymentsFiles = ['proxy.yaml', 'deployment.yaml', 'secret.yaml'];
180
180
  // remove engine-private of .dockerignore for local testing
181
181
  for (const file of deploymentsFiles) {
182
182
  if (fs.existsSync(`./manifests/deployment/${confName}-${env}/${file}`)) {
package/bin/index.js CHANGED
@@ -135,13 +135,15 @@ program
135
135
 
136
136
  program
137
137
  .command('dockerfile-image-build')
138
- .argument('<deploy-id>', 'Deploy configuration id')
139
- .argument('[env]', 'Optional environment, for default is development')
140
- .argument('[path]', 'Absolute or relative directory, for default is current')
141
- .option('--image-archive', 'Only load tar image from ./images')
142
- .option('--podman-save', 'Save image from podman to ./images')
143
- .option('--image-name <image-name>', 'Set custom image name')
144
- .option('--image-version <image-version>', 'Set custom image version')
138
+ .option('--path [path]', 'Dockerfile path')
139
+ .option('--image-name [image-name]', 'Set image name')
140
+ .option('--image-path [image-path]', 'Set tar image path')
141
+ .option('--dockerfile-name [dockerfile-name]', 'set Dockerfile name')
142
+ .option('--podman-save', 'Export tar file from podman')
143
+ .option('--kind-load', 'Import tar image to Kind cluster')
144
+ .option('--secrets', 'Dockerfile env secrets')
145
+ .option('--secrets-path [secrets-path]', 'Dockerfile custom path env secrets')
146
+ .option('--no-cache', 'Build without using cache')
145
147
  .description('Build image from Dockerfile')
146
148
  .action(Underpost.image.dockerfile.build);
147
149
 
@@ -224,8 +226,10 @@ program
224
226
  .command('monitor')
225
227
  .argument('<deploy-id>', 'Deploy configuration id')
226
228
  .argument('[env]', 'Optional environment, for default is development')
229
+ .option('--ms-interval <ms-interval>', 'Custom ms interval delta time')
230
+ .option('--now', 'Exec immediately monitor script')
231
+ .option('--single', 'Disable recurrence')
227
232
  .description('Monitor health server management')
228
- .option('--itc', 'Inside container execution context')
229
233
  .action(Underpost.monitor.callback);
230
234
 
231
235
  program.parse();
@@ -58,7 +58,7 @@ services:
58
58
  cpus: '0.25'
59
59
  memory: 20M
60
60
  labels: # labels in Compose file instead of Dockerfile
61
- engine.version: '2.8.635'
61
+ engine.version: '2.8.637'
62
62
  networks:
63
63
  - load-balancer
64
64
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "type": "module",
3
3
  "main": "src/index.js",
4
4
  "name": "underpost",
5
- "version": "2.8.635",
5
+ "version": "2.8.637",
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",
package/src/cli/fs.js CHANGED
@@ -38,7 +38,7 @@ class UnderpostFileStorage {
38
38
  options = { rm: false, recursive: false, deployId: '', force: false, pull: false, git: false },
39
39
  ) {
40
40
  const { storage, storageConf } = UnderpostFileStorage.API.getStorageConf(options);
41
- const deleteFiles = UnderpostRepository.API.getDeleteFiles(path);
41
+ const deleteFiles = options.pull === true ? [] : UnderpostRepository.API.getDeleteFiles(path);
42
42
  for (const relativePath of deleteFiles) {
43
43
  const _path = path + '/' + relativePath;
44
44
  if (_path in storage) {
@@ -46,10 +46,6 @@ class UnderpostFileStorage {
46
46
  delete storage[_path];
47
47
  }
48
48
  }
49
- const files =
50
- options.git === true
51
- ? UnderpostRepository.API.getChangedFiles(path)
52
- : await fs.readdir(path, { recursive: true });
53
49
  if (options.pull === true) {
54
50
  for (const _path of Object.keys(storage)) {
55
51
  if (!fs.existsSync(_path) || options.force === true) {
@@ -57,7 +53,11 @@ class UnderpostFileStorage {
57
53
  await UnderpostFileStorage.API.pull(_path, options);
58
54
  } else logger.warn(`Pull path already exists`, _path);
59
55
  }
60
- } else
56
+ } else {
57
+ const files =
58
+ options.git === true
59
+ ? UnderpostRepository.API.getChangedFiles(path)
60
+ : await fs.readdir(path, { recursive: true });
61
61
  for (const relativePath of files) {
62
62
  const _path = path + '/' + relativePath;
63
63
  if (fs.statSync(_path).isDirectory()) {
@@ -68,6 +68,7 @@ class UnderpostFileStorage {
68
68
  if (storage) storage[_path] = {};
69
69
  } else logger.warn('File already exists', _path);
70
70
  }
71
+ }
71
72
  UnderpostFileStorage.API.writeStorageConf(storage, storageConf);
72
73
  if (options.git === true) {
73
74
  shellExec(`cd ${path} && git add .`);
package/src/cli/image.js CHANGED
@@ -18,41 +18,49 @@ class UnderpostImage {
18
18
  shellExec(`sudo podman pull docker.io/library/debian:buster`);
19
19
  },
20
20
  build(
21
- deployId = 'default',
22
- env = 'development',
23
- path = '.',
24
- options = { imageArchive: false, podmanSave: false, imageName: '', imageVersion: '' },
21
+ options = {
22
+ path: '',
23
+ imageName: '',
24
+ imagePath: '',
25
+ dockerfileName: '',
26
+ podmanSave: false,
27
+ kindLoad: false,
28
+ secrets: false,
29
+ secretsPath: '',
30
+ noCache: false,
31
+ },
25
32
  ) {
26
- const imgName = `${
27
- options.imageName && typeof options.imageName === 'string' ? options.imageName : `${deployId}-${env}`
28
- }:${
29
- options.imageVersion && typeof options.imageVersion === 'string' ? options.imageVersion : Underpost.version
30
- }`;
31
- const podManImg = `localhost/${imgName}`;
32
- const imagesStoragePath = `/images`;
33
- if (!fs.existsSync(`${path}${imagesStoragePath}`))
34
- fs.mkdirSync(`${path}${imagesStoragePath}`, { recursive: true });
35
- const tarFile = `.${imagesStoragePath}/${imgName.replace(':', '_')}.tar`;
36
-
37
- let secrets = ' ';
33
+ const { path, imageName, imagePath, dockerfileName, podmanSave, secrets, secretsPath, kindLoad, noCache } =
34
+ options;
35
+ const podManImg = `localhost/${imageName}`;
36
+ if (imagePath && typeof imagePath === 'string' && !fs.existsSync(imagePath))
37
+ fs.mkdirSync(imagePath, { recursive: true });
38
+ const tarFile = `${imagePath}/${imageName.replace(':', '_')}.tar`;
39
+ let secretsInput = ' ';
38
40
  let secretDockerInput = '';
39
-
40
- const envObj = dotenv.parse(fs.readFileSync(`${getNpmRootPath()}/underpost/.env`, 'utf8'));
41
-
42
- for (const key of Object.keys(envObj)) {
43
- continue;
44
- secrets += ` && export ${key}="${envObj[key]}" `; // $(cat gitlab-token.txt)
45
- secretDockerInput += ` --secret id=${key},env=${key} \ `;
41
+ let cache = '';
42
+ if (secrets === true) {
43
+ const envObj = dotenv.parse(
44
+ fs.readFileSync(
45
+ secretsPath && typeof secretsPath === 'string' ? secretsPath : `${getNpmRootPath()}/underpost/.env`,
46
+ 'utf8',
47
+ ),
48
+ );
49
+ for (const key of Object.keys(envObj)) {
50
+ secretsInput += ` && export ${key}="${envObj[key]}" `; // $(cat gitlab-token.txt)
51
+ secretDockerInput += ` --secret id=${key},env=${key} \ `;
52
+ }
46
53
  }
47
- // --rm --no-cache
48
- if (options.imageArchive !== true) {
54
+ if (noCache === true) cache += ' --rm --no-cache';
55
+ if (path && typeof path === 'string')
49
56
  shellExec(
50
- `cd ${path}${secrets}&& sudo podman build -f ./Dockerfile -t ${imgName} --pull=never --cap-add=CAP_AUDIT_WRITE${secretDockerInput}`,
57
+ `cd ${path}${secretsInput}&& sudo podman build -f ./${
58
+ dockerfileName && typeof dockerfileName === 'string' ? dockerfileName : 'Dockerfile'
59
+ } -t ${imageName} --pull=never --cap-add=CAP_AUDIT_WRITE${cache}${secretDockerInput}`,
51
60
  );
52
- }
53
- if (options.imageArchive !== true || options.podmanSave === true)
54
- shellExec(`cd ${path} && podman save -o ${tarFile} ${podManImg}`);
55
- shellExec(`cd ${path} && sudo kind load image-archive ${tarFile}`);
61
+
62
+ if (podmanSave === true) shellExec(`podman save -o ${tarFile} ${podManImg}`);
63
+ if (kindLoad === true) shellExec(`sudo kind load image-archive ${tarFile}`);
56
64
  },
57
65
  async script(deployId = 'default', env = 'development', options = { run: false, build: false }) {
58
66
  if (options.build === true) {
@@ -4,13 +4,12 @@ import UnderpostDeploy from './deploy.js';
4
4
  import axios from 'axios';
5
5
  import UnderpostRootEnv from './env.js';
6
6
  import fs from 'fs-extra';
7
- import { timer } from '../client/components/core/CommonJs.js';
8
7
 
9
8
  const logger = loggerFactory(import.meta);
10
9
 
11
10
  class UnderpostMonitor {
12
11
  static API = {
13
- async callback(deployId, env = 'development', options = { itc: false }) {
12
+ async callback(deployId, env = 'development', options = { now: false, single: false, msInterval: '' }) {
14
13
  const router = await UnderpostDeploy.API.routerFactory(deployId, env);
15
14
 
16
15
  const confServer = loadReplicas(
@@ -22,45 +21,70 @@ class UnderpostMonitor {
22
21
 
23
22
  logger.info('', pathPortAssignmentData);
24
23
 
25
- if (options.itc === true) {
26
- const errorPayloads = [];
27
- const maxAttempts = Object.keys(pathPortAssignmentData)
28
- .map((host) => pathPortAssignmentData[host].length)
29
- .reduce((accumulator, value) => accumulator + value, 0);
24
+ const errorPayloads = [];
25
+ const maxAttempts = Object.keys(pathPortAssignmentData)
26
+ .map((host) => pathPortAssignmentData[host].length)
27
+ .reduce((accumulator, value) => accumulator + value, 0);
30
28
 
31
- const monitor = async () => {
32
- await timer(30000);
33
- if (UnderpostRootEnv.API.get('running-job')) return await monitor();
34
- for (const host of Object.keys(pathPortAssignmentData)) {
35
- for (const instance of pathPortAssignmentData[host]) {
36
- const { port, path } = instance;
37
- if (path.match('peer') || path.match('socket')) continue;
38
- const urlTest = `http://localhost:${port}${path}`;
39
- logger.info('Test instance', urlTest);
40
- await axios.get(urlTest, { timeout: 10000 }).catch((error) => {
41
- // console.log(error);
42
- const errorPayload = {
43
- urlTest,
44
- host,
45
- port,
46
- path,
47
- name: error.name,
48
- status: error.status,
49
- code: error.code,
50
- errors: error.errors,
51
- };
52
- if (errorPayload.status !== 404) {
53
- errorPayloads.push(errorPayload);
54
- if (errorPayloads.length >= maxAttempts) throw new Error(JSON.stringify(errorPayloads, null, 4));
55
- logger.error('Error accumulator', errorPayloads.length);
29
+ const monitor = async (reject) => {
30
+ logger.info('Check server health');
31
+ for (const host of Object.keys(pathPortAssignmentData)) {
32
+ for (const instance of pathPortAssignmentData[host]) {
33
+ const { port, path } = instance;
34
+ if (path.match('peer') || path.match('socket')) continue;
35
+ const urlTest = `http://localhost:${port}${path}`;
36
+ // logger.info('Test instance', urlTest);
37
+ await axios.get(urlTest, { timeout: 10000 }).catch((error) => {
38
+ // console.log(error);
39
+ const errorPayload = {
40
+ urlTest,
41
+ host,
42
+ port,
43
+ path,
44
+ name: error.name,
45
+ status: error.status,
46
+ code: error.code,
47
+ errors: error.errors,
48
+ };
49
+ if (errorPayload.status !== 404) {
50
+ errorPayloads.push(errorPayload);
51
+ if (errorPayloads.length >= maxAttempts) {
52
+ const message = JSON.stringify(errorPayloads, null, 4);
53
+ if (reject) reject(message);
54
+ else throw new Error(message);
56
55
  }
57
- });
58
- }
56
+ logger.error('Error accumulator', errorPayloads.length);
57
+ }
58
+ });
59
59
  }
60
- await monitor();
61
- };
62
- await monitor();
63
- }
60
+ }
61
+ };
62
+ if (options.now === true) await monitor();
63
+ if (options.single === true) return;
64
+ let optionsMsTimeout = parseInt(options.msInterval);
65
+ if (isNaN(optionsMsTimeout)) optionsMsTimeout = 30000;
66
+ const monitorCallBack = (resolve, reject) => {
67
+ const envMsTimeout = UnderpostRootEnv.API.get('monitor-ms');
68
+ setTimeout(
69
+ async () => {
70
+ switch (UnderpostRootEnv.API.get('monitor-input')) {
71
+ case 'pause':
72
+ monitorCallBack(resolve, reject);
73
+ return;
74
+ case 'restart':
75
+ return reject();
76
+ case 'stop':
77
+ return resolve();
78
+ default:
79
+ await monitor(reject);
80
+ monitorCallBack(resolve, reject);
81
+ return;
82
+ }
83
+ },
84
+ !isNaN(envMsTimeout) ? envMsTimeout : optionsMsTimeout,
85
+ );
86
+ };
87
+ return await new Promise((...args) => monitorCallBack(...args));
64
88
  },
65
89
  };
66
90
  }
@@ -252,7 +252,7 @@ const Account = {
252
252
  ${options?.bottomRender ? await options.bottomRender() : ``}
253
253
  <div class="in hide">
254
254
  ${await BtnIcon.Render({
255
- class: 'section-mp form-button btn-account',
255
+ class: 'in section-mp form-button btn-account',
256
256
  label: Translate.Render('update'),
257
257
  type: 'submit',
258
258
  })}
@@ -260,13 +260,13 @@ const Account = {
260
260
  </form>
261
261
  <div class="in">
262
262
  ${await BtnIcon.Render({
263
- class: 'section-mp form-button btn-account-delete hide',
263
+ class: 'in section-mp form-button btn-account-delete hide',
264
264
  label: html` ${Translate.Render(`delete-account`)}`,
265
265
  type: 'button',
266
266
  style: 'color: #5f5f5f',
267
267
  })}
268
268
  ${await BtnIcon.Render({
269
- class: 'section-mp form-button btn-account-delete-confirm',
269
+ class: 'in section-mp form-button btn-account-delete-confirm',
270
270
  label: html` ${Translate.Render(`delete-account`)}`,
271
271
  type: 'button',
272
272
  style: 'color: #5f5f5f',
@@ -25,7 +25,7 @@ const BlockChainManagement = {
25
25
  placeholder: true,
26
26
  })}
27
27
  ${await BtnIcon.Render({
28
- class: `section-mp btn-custom btn-upload-blockchain`,
28
+ class: `inl section-mp btn-custom btn-upload-blockchain`,
29
29
  label: html`<i class="fas fa-plus"></i> ${Translate.Render(`create`)}`,
30
30
  })}
31
31
  </div>
@@ -409,9 +409,11 @@ const CalendarCore = {
409
409
  if (delayBlock) return;
410
410
  else {
411
411
  delayBlock = true;
412
+ const _currentPath = `${location.pathname}${location.search}`;
412
413
  setTimeout(() => {
413
414
  delayBlock = false;
414
- }, 500);
415
+ if (`${location.pathname}${location.search}` !== _currentPath) this.Data[options.idModal].updatePanel();
416
+ }, 1000);
415
417
  }
416
418
  const cid = getQueryParams().cid ? getQueryParams().cid : '';
417
419
  if (options.route === 'home') Modal.homeCid = newInstance(cid);
@@ -521,7 +523,7 @@ const CalendarCore = {
521
523
  <div class="in calendar-container hide">
522
524
  <div class="stq modal calendar-buttons-container">
523
525
  ${await BtnIcon.Render({
524
- class: `section-mp btn-custom close-calendar-container flr`,
526
+ class: `inl section-mp btn-custom close-calendar-container flr`,
525
527
  label: html`<i class="fa-solid fa-xmark"></i> ${Translate.Render('close')}`,
526
528
  type: 'button',
527
529
  })}
@@ -396,7 +396,8 @@ const CssCoreDark = {
396
396
  text-align: center;
397
397
  background: #1a1a1a;
398
398
  font-size: 17px;
399
- height: 35px;
399
+ height: 30px;
400
+ padding: 5px 0px 5px 0px;
400
401
  }
401
402
  ::placeholder {
402
403
  color: #c6c4c4;
@@ -471,6 +472,7 @@ const CssCoreDark = {
471
472
  width: 260px;
472
473
  font-size: 20px;
473
474
  padding: 10px;
475
+ min-height: 45px;
474
476
  }
475
477
  .toggle-form-container {
476
478
  border: 2px solid #313131;
@@ -494,6 +496,7 @@ const CssCoreDark = {
494
496
  font-size: 20px;
495
497
  padding: 10px;
496
498
  text-align: center;
499
+ min-height: 45px;
497
500
  }
498
501
  .drop-zone-file-explorer {
499
502
  min-height: 300px;
@@ -700,7 +703,8 @@ const CssCoreLight = {
700
703
  text-align: center;
701
704
  background: #eaeaea;
702
705
  font-size: 17px;
703
- height: 35px;
706
+ height: 30px;
707
+ padding: 5px 0px 5px 0px;
704
708
  }
705
709
  ::placeholder {
706
710
  color: #333;
@@ -776,6 +780,7 @@ const CssCoreLight = {
776
780
  width: 260px;
777
781
  font-size: 20px;
778
782
  padding: 10px;
783
+ min-height: 45px;
779
784
  }
780
785
  .toggle-form-container {
781
786
  border-radius: 5px;
@@ -800,6 +805,7 @@ const CssCoreLight = {
800
805
  font-size: 20px;
801
806
  padding: 10px;
802
807
  text-align: center;
808
+ min-height: 45px;
803
809
  }
804
810
  .input-container-width {
805
811
  cursor: pointer;
@@ -75,28 +75,12 @@ const LoadingAnimation = {
75
75
  const style = {
76
76
  'text-align': 'center',
77
77
  };
78
- if (s(container).classList) {
79
- const classes = Array.from(s(container).classList);
80
- if (classes.find((e) => e.match('management-table-btn-mini'))) {
81
- style.top = '-2px';
82
- style.left = '-2px';
83
- } else if (classes.find((e) => e.match('-btn-tool'))) {
84
- style.top = '-26px';
85
- style.left = '-10px';
86
- } else if (classes.find((e) => e.match('main-btn-')) && !classes.find((e) => e.match('main-btn-square-menu'))) {
87
- style.top = '-8px';
88
- style.left = '-10px';
89
- } else if (classes.find((e) => e.match('action-bar-box'))) {
90
- style.top = '-30px';
91
- style.left = '-12px';
92
- }
93
- }
94
78
 
95
79
  append(
96
80
  container,
97
81
  html`
98
82
  <div
99
- class="in ${id}"
83
+ class="abs center ${id}"
100
84
  style="${renderCssAttr({
101
85
  style,
102
86
  })}"
@@ -135,7 +135,7 @@ const LogIn = {
135
135
  return html`
136
136
  <div class="in">
137
137
  ${await BtnIcon.Render({
138
- class: 'section-mp form-button btn-log-in-i-not-have-account',
138
+ class: 'in section-mp form-button btn-log-in-i-not-have-account',
139
139
  label: html`<i class="fas fa-user-plus"></i> ${Translate.Render(`i-not-have-account`)}
140
140
  <br />
141
141
  ${Translate.Render(`sign-up`)}`,
@@ -165,7 +165,7 @@ const LogIn = {
165
165
  </div>
166
166
  <div class="in">
167
167
  ${await BtnIcon.Render({
168
- class: 'section-mp form-button btn-log-in-forgot-password',
168
+ class: 'in section-mp form-button btn-log-in-forgot-password',
169
169
  label: html`<i class="fas fa-question-circle"></i> ${Translate.Render(`forgot-password`)}`,
170
170
  type: 'button',
171
171
  })}
@@ -181,7 +181,7 @@ const LogIn = {
181
181
 
182
182
  <div class="in">
183
183
  ${await BtnIcon.Render({
184
- class: 'section-mp form-button btn-log-in',
184
+ class: 'in section-mp form-button btn-log-in',
185
185
  label: Translate.Render('log-in'),
186
186
  type: 'submit',
187
187
  })}
@@ -47,7 +47,7 @@ const LogOut = {
47
47
  return html` <form class="in">
48
48
  <div class="in">
49
49
  ${await BtnIcon.Render({
50
- class: 'section-mp btn-custom btn-log-out',
50
+ class: 'inl section-mp btn-custom btn-log-out',
51
51
  label: html`<i class="fa-solid fa-power-off"></i> ${Translate.Render('log-out')}`,
52
52
  type: 'submit',
53
53
  })}
@@ -246,7 +246,7 @@ const Panel = {
246
246
  let render = '';
247
247
  let renderForm = html` <div class="in modal stq" style="top: 0px; z-index: 1; padding-bottom: 5px">
248
248
  ${await BtnIcon.Render({
249
- class: `section-mp btn-custom btn-${idPanel}-close`,
249
+ class: `inl section-mp btn-custom btn-${idPanel}-close`,
250
250
  label: html`<i class="fa-solid fa-xmark"></i> ${Translate.Render('close')}`,
251
251
  type: 'button',
252
252
  })}
@@ -404,13 +404,13 @@ const Panel = {
404
404
  }
405
405
  let renderFormBtn = html`
406
406
  ${await BtnIcon.Render({
407
- class: `section-mp btn-custom btn-${idPanel}-submit`,
407
+ class: `inl section-mp btn-custom btn-${idPanel}-submit`,
408
408
  label: html`<span class="btn-${idPanel}-label-add"><i class="fas fa-plus"></i> ${Translate.Render('add')}</span
409
409
  ><span class="btn-${idPanel}-label-edit hide"><i class="fas fa-edit"></i> ${Translate.Render('edit')}</span>`,
410
410
  type: 'submit',
411
411
  })}
412
412
  ${await BtnIcon.Render({
413
- class: `section-mp btn-custom btn-${idPanel}-clean`,
413
+ class: `inl section-mp btn-custom btn-${idPanel}-clean`,
414
414
  label: html`<i class="fa-solid fa-broom"></i> ${Translate.Render('clear')}`,
415
415
  type: 'button',
416
416
  })}
@@ -546,7 +546,7 @@ const Panel = {
546
546
  s(`.${btnSelector}`).onclick = () => dataBtn.onClick();
547
547
  });
548
548
  customButtonsRender += ` ${await BtnIcon.Render({
549
- class: `section-mp btn-custom ${btnSelector}`,
549
+ class: `inl section-mp btn-custom ${btnSelector}`,
550
550
  label: dataBtn.label,
551
551
  type: 'button',
552
552
  })}`;
@@ -656,7 +656,7 @@ const Panel = {
656
656
  >
657
657
  <div class="in ${idPanel}-form-header">
658
658
  ${await BtnIcon.Render({
659
- class: `section-mp btn-custom btn-${idPanel}-add ${
659
+ class: `inl section-mp btn-custom btn-${idPanel}-add ${
660
660
  options?.role?.add ? (!options.role.add() ? 'hide' : '') : ''
661
661
  }`,
662
662
  label: html`<i class="fas fa-plus"></i> ${Translate.Render('add')}`,
@@ -430,9 +430,11 @@ const PanelForm = {
430
430
  if (delayBlock) return;
431
431
  else {
432
432
  delayBlock = true;
433
+ const _currentPath = `${location.pathname}${location.search}`;
433
434
  setTimeout(() => {
434
435
  delayBlock = false;
435
- }, 500);
436
+ if (`${location.pathname}${location.search}` !== _currentPath) this.Data[idPanel].updatePanel();
437
+ }, 1000);
436
438
  }
437
439
  const cid = getQueryParams().cid ? getQueryParams().cid : '';
438
440
  if (options.route === 'home') Modal.homeCid = newInstance(cid);
@@ -126,7 +126,7 @@ const Recover = {
126
126
  });
127
127
  return html`
128
128
  ${await BtnIcon.Render({
129
- class: 'section-mp form-button btn-recover-log-in hide',
129
+ class: 'in section-mp form-button btn-recover-log-in hide',
130
130
  label: Translate.Render('log-in'),
131
131
  type: 'button',
132
132
  })}
@@ -181,7 +181,7 @@ const Recover = {
181
181
  ${options?.bottomRender ? await options.bottomRender() : ``}
182
182
  <div class="in recover-send-btn-container">
183
183
  ${await BtnIcon.Render({
184
- class: 'section-mp form-button btn-recover',
184
+ class: 'in section-mp form-button btn-recover',
185
185
  label: Translate.Render(mode === 'recover-verify-email' ? 'send-recover-verify-email' : 'change-password'),
186
186
  type: 'button',
187
187
  })}
@@ -191,7 +191,7 @@ const Recover = {
191
191
  <i class="fa-solid fa-triangle-exclamation"></i> ${Translate.Render('15-min-valid-recover-email')}
192
192
  </div>
193
193
  ${await BtnIcon.Render({
194
- class: 'section-mp form-button btn-recover-resend',
194
+ class: 'in section-mp form-button btn-recover-resend',
195
195
  label: html`${Translate.Render('resend')} ${Translate.Render('recover-verify-email')}`,
196
196
  type: 'submit',
197
197
  })}
@@ -63,7 +63,7 @@ const SignUp = {
63
63
  });
64
64
  return html`
65
65
  ${await BtnIcon.Render({
66
- class: 'section-mp form-button btn-sign-up-i-have-account',
66
+ class: 'in section-mp form-button btn-sign-up-i-have-account',
67
67
  label: html`<i class="fas fa-sign-in-alt"></i> ${Translate.Render('i-have-account')}<br />${Translate.Render(
68
68
  'log-in',
69
69
  )}`,
@@ -112,7 +112,7 @@ const SignUp = {
112
112
  ${options?.bottomRender ? await options.bottomRender() : ``}
113
113
  <div class="in">
114
114
  ${await BtnIcon.Render({
115
- class: 'section-mp form-button btn-sign-up',
115
+ class: 'in section-mp form-button btn-sign-up',
116
116
  label: Translate.Render('sign-up'),
117
117
  type: 'submit',
118
118
  })}
@@ -133,6 +133,8 @@ SrrComponent = ({ title, ssrPath, buildId, ssrHeadComponents, ssrBodyComponents,
133
133
  border: none;
134
134
  padding-block: 0;
135
135
  padding-inline: 0;
136
+ height: 30px;
137
+ line-height: 30px;
136
138
  }
137
139
  input::file-selector-button {
138
140
  outline: none !important;
package/src/index.js CHANGED
@@ -29,7 +29,7 @@ class Underpost {
29
29
  * @type {String}
30
30
  * @memberof Underpost
31
31
  */
32
- static version = 'v2.8.635';
32
+ static version = 'v2.8.637';
33
33
  /**
34
34
  * Repository cli API
35
35
  * @static
@@ -0,0 +1,65 @@
1
+ ARG BASE_DEBIAN=buster
2
+
3
+ USER root
4
+
5
+ FROM debian:${BASE_DEBIAN}
6
+
7
+ ENV DEBIAN_FRONTEND=noninteractive
8
+
9
+ # Set root password to root, format is 'user:password'.
10
+ RUN echo 'root:root' | chpasswd
11
+
12
+ RUN apt-get update --fix-missing
13
+ RUN apt-get upgrade -y
14
+ # install sudo
15
+ RUN apt-get -y install sudo
16
+ # net-tools provides netstat commands
17
+ RUN apt-get -y install curl net-tools
18
+ RUN apt-get -yq install openssh-server supervisor
19
+ # Few handy utilities which are nice to have
20
+ RUN apt-get -y install nano vim less --no-install-recommends
21
+ RUN apt-get clean
22
+
23
+ # install ssh
24
+ RUN mkdir -p /var/run/sshd
25
+ # Allow root login via password
26
+ RUN sed -ri 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
27
+
28
+ # install open ssl git and others tools
29
+ RUN apt-get install -yq --no-install-recommends libssl-dev curl wget git gnupg
30
+
31
+ # install lampp
32
+ RUN curl -Lo xampp-linux-installer.run https://sourceforge.net/projects/xampp/files/XAMPP%20Linux/7.4.33/xampp-linux-x64-7.4.33-0-installer.run?from_af=true
33
+ RUN chmod +x xampp-linux-installer.run
34
+ RUN bash -c './xampp-linux-installer.run'
35
+ RUN ln -sf /opt/lampp/lampp /usr/bin/lampp
36
+ # Enable XAMPP web interface(remove security checks)
37
+ RUN sed -i.bak s'/Require local/Require all granted/g' /opt/lampp/etc/extra/httpd-xampp.conf
38
+ # Enable error display in php
39
+ RUN sed -i.bak s'/display_errors=Off/display_errors=On/g' /opt/lampp/etc/php.ini
40
+ # Enable includes of several configuration files
41
+ RUN mkdir /opt/lampp/apache2/conf.d
42
+ RUN echo "IncludeOptional /opt/lampp/apache2/conf.d/*.conf" >>/opt/lampp/etc/httpd.conf
43
+ # Create a /www folder and a symbolic link to it in /opt/lampp/htdocs. It'll be accessible via http://localhost:[port]/www/
44
+ # This is convenient because it doesn't interfere with xampp, phpmyadmin or other tools in /opt/lampp/htdocs
45
+ # /opt/lampp/etc/httpd.conf
46
+ RUN mkdir /www
47
+ RUN ln -s /www /opt/lampp/htdocs
48
+
49
+ # install nodejs https://github.com/nodesource/distributions/blob/master/README.md#deb
50
+ RUN curl -fsSL https://deb.nodesource.com/setup_23.x | bash -
51
+ RUN apt-get install -y nodejs build-essential
52
+ RUN node --version
53
+ RUN npm --version
54
+
55
+ WORKDIR /home/dd
56
+
57
+ EXPOSE 22
58
+
59
+ EXPOSE 80
60
+
61
+ EXPOSE 443
62
+
63
+ EXPOSE 3000-3100
64
+
65
+ EXPOSE 4000-4100