@underpostnet/underpost 2.99.0 → 2.99.4

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.
@@ -687,6 +687,10 @@ const TranslateCore = {
687
687
  en: 'Markdown source copied to clipboard',
688
688
  es: 'Fuente de Markdown copiada al portapapeles',
689
689
  };
690
+ Translate.Data['success-clear-filter'] = {
691
+ en: 'Filter cleared successfully',
692
+ es: 'Filtro limpiado con éxito',
693
+ };
690
694
  },
691
695
  };
692
696
 
@@ -39,9 +39,19 @@ const DefaultOptions = {
39
39
  const columnDefFormatter = (obj, columnDefs, customFormat) => {
40
40
  for (const colDef of columnDefs)
41
41
  switch (colDef.cellDataType) {
42
- case 'date':
43
- obj[colDef.field] = obj[colDef.field] ? new Date(obj[colDef.field]) : new Date();
42
+ case 'date': {
43
+ const value = obj[colDef.field];
44
+
45
+ // Do NOT default missing/blank dates to "now" — render as empty instead.
46
+ if (value === null || value === undefined || value === '') {
47
+ obj[colDef.field] = null;
48
+ break;
49
+ }
50
+
51
+ const date = new Date(value);
52
+ obj[colDef.field] = isNaN(date.getTime()) ? null : date;
44
53
  break;
54
+ }
45
55
  case 'boolean':
46
56
  if (obj[colDef.field] !== true && obj[colDef.field] !== false) obj[colDef.field] = false;
47
57
  default:
package/src/index.js CHANGED
@@ -25,6 +25,7 @@ import UnderpostDns from './server/dns.js';
25
25
  import UnderpostBackup from './server/backup.js';
26
26
  import UnderpostCron from './server/cron.js';
27
27
  import UnderpostStartUp from './server/start.js';
28
+ import UnderpostTls from './server/tls.js';
28
29
 
29
30
  /**
30
31
  * Underpost main module methods
@@ -38,7 +39,7 @@ class Underpost {
38
39
  * @type {String}
39
40
  * @memberof Underpost
40
41
  */
41
- static version = 'v2.99.0';
42
+ static version = 'v2.99.4';
42
43
  /**
43
44
  * Repository cli API
44
45
  * @static
@@ -228,6 +229,16 @@ class Underpost {
228
229
  static get start() {
229
230
  return UnderpostStartUp.API;
230
231
  }
232
+
233
+ /**
234
+ * TLS/SSL server utilities API
235
+ * @static
236
+ * @type {UnderpostTls.API}
237
+ * @memberof Underpost
238
+ */
239
+ static get tls() {
240
+ return UnderpostTls.API;
241
+ }
231
242
  }
232
243
 
233
244
  const up = Underpost;
@@ -258,6 +269,7 @@ export {
258
269
  UnderpostBackup,
259
270
  UnderpostCron,
260
271
  UnderpostStartUp,
272
+ UnderpostTls,
261
273
  };
262
274
 
263
275
  export default Underpost;
@@ -20,7 +20,7 @@ import { createPeerServer } from '../../server/peer.js';
20
20
  import { createValkeyConnection } from '../../server/valkey.js';
21
21
  import { applySecurity, authMiddlewareFactory } from '../../server/auth.js';
22
22
  import { ssrMiddlewareFactory } from '../../server/ssr.js';
23
- import { TLS } from '../../server/tls.js';
23
+
24
24
  import { shellExec } from '../../server/process.js';
25
25
  import { devProxyHostFactory, isDevProxyContext, isTlsDevProxy } from '../../server/conf.js';
26
26
 
@@ -249,8 +249,8 @@ class ExpressService {
249
249
 
250
250
  // Start listening on the main port
251
251
  if (useLocalSsl && process.env.NODE_ENV === 'development') {
252
- if (!TLS.validateSecureContext()) shellExec(`node bin/deploy tls`);
253
- const { ServerSSL } = await TLS.createSslServer(app);
252
+ if (!Underpost.tls.validateSecureContext()) shellExec(`node bin/deploy tls`);
253
+ const { ServerSSL } = await Underpost.tls.createSslServer(app);
254
254
  await Underpost.start.listenPortController(ServerSSL, port, runningData);
255
255
  } else await Underpost.start.listenPortController(server, port, runningData);
256
256
 
@@ -66,7 +66,7 @@ const Config = {
66
66
  /**
67
67
  * @method deployIdFactory
68
68
  * @description Creates a new deploy ID.
69
- * @param {string} [deployId='dd-default
69
+ * @param {string} [deployId='dd-default']
70
70
  * @param {object} [options={ subConf: '', cluster: false }] - The options.
71
71
  * @memberof ServerConfBuilder
72
72
  */
@@ -842,8 +842,10 @@ const buildPortProxyRouter = (
842
842
 
843
843
  if (options.devProxyContext === true && process.env.NODE_ENV === 'development') {
844
844
  const confDevApiServer = JSON.parse(
845
- fs.readFileSync(`./engine-private/conf/${process.argv[3]}/conf.server.dev.${process.argv[4]}-dev-api.json`),
846
- 'utf8',
845
+ fs.readFileSync(
846
+ `./engine-private/conf/${process.argv[3]}/conf.server.dev.${process.argv[4]}-dev-api.json`,
847
+ 'utf8',
848
+ ),
847
849
  );
848
850
  let devApiHosts = [];
849
851
  let origins = [];
@@ -899,7 +901,7 @@ const buildReplicaId = ({ deployId, replica }) => `${deployId}-${replica.slice(1
899
901
  * @description Gets the data deploy.
900
902
  * @param {object} options - The options.
901
903
  * @param {boolean} [options.buildSingleReplica=false] - The build single replica.
902
- * @param {string} options.deployId - The deploy ID.
904
+ * @param {string} [options.deployId] - The deploy ID.
903
905
  * @param {boolean} [options.disableSyncEnvPort=false] - The disable sync env port.
904
906
  * @returns {object} - The data deploy.
905
907
  * @memberof ServerConfBuilder
@@ -98,7 +98,7 @@ const setUpInfo = async (logger = new winston.Logger()) => {
98
98
  * messages.
99
99
  * @param meta - The `meta` parameter in the `loggerFactory` function is used to extract the last part
100
100
  * of a URL and use it to create log files in a specific directory.
101
- * @returns {winston.Logger} The `loggerFactory` function returns a logger instance created using Winston logger
101
+ * @returns {underpostLogger} The `loggerFactory` function returns a logger instance created using Winston logger
102
102
  * library. The logger instance is configured with various transports for printing out messages to
103
103
  * different destinations such as the terminal, error.log file, and all.log file. The logger instance
104
104
  * also has a method `setUpInfo` attached to it for setting up additional information.
@@ -133,9 +133,16 @@ const loggerFactory = (meta = { url: '' }) => {
133
133
  // rejectionHandlers: [new winston.transports.File({ filename: 'rejections.log' })],
134
134
  // exitOnError: false,
135
135
  });
136
- logger.setUpInfo = async () => {
137
- await setUpInfo(logger);
138
- };
136
+ /**
137
+ * The returned logger is a real Winston logger instance with an extra `setUpInfo` method.
138
+ *
139
+ * @memberof Logger
140
+ * @typedef {winston.Logger & {
141
+ * setUpInfo: (logger?: winston.Logger) => Promise<void>
142
+ * }} underpostLogger
143
+ */
144
+ logger.setUpInfo = () => setUpInfo(logger);
145
+
139
146
  return logger;
140
147
  };
141
148
 
@@ -12,6 +12,7 @@ import dotenv from 'dotenv';
12
12
  import { loggerFactory } from './logger.js';
13
13
  import clipboard from 'clipboardy';
14
14
  import Underpost from '../index.js';
15
+ import { getNpmRootPath } from './conf.js';
15
16
 
16
17
  dotenv.config();
17
18
 
@@ -128,16 +129,40 @@ const shellCd = (cd, options = { disableLog: false }) => {
128
129
  * @param {string} cmd - The command to execute in the new terminal.
129
130
  * @param {Object} [options] - Options for the terminal opening.
130
131
  * @param {boolean} [options.single=false] - If true, execute as a single session process using `setsid`.
132
+ * @param {string} [options.chown] - Path to change ownership to the target user.
131
133
  * @returns {void}
132
134
  */
133
135
  const openTerminal = (cmd, options = { single: false }) => {
136
+ // Find the graphical user's UID from /run/user (prefer non-root UID, usually 1000)
137
+ const IDS = shellExec(`ls -1 /run/user`, { stdout: true, silent: true })
138
+ .split('\n')
139
+ .map((v) => v.trim())
140
+ .filter(Boolean);
141
+
142
+ const nonRootIds = IDS.filter((id) => id !== '0');
143
+ const ID = nonRootIds.length > 0 ? nonRootIds[0] : IDS[0];
144
+
145
+ if (!options.chown) options.chown = `/home/dd ${getNpmRootPath()}/underpost`;
146
+
147
+ shellExec(`chown -R ${ID}:${ID} ${options.chown}`);
148
+
149
+ // Run the terminal as the graphical user and use THAT user's runtime dir/bus.
150
+ const confCmd = `USER_GRAPHICAL=$(getent passwd "${ID}" | cut -d: -f1); \
151
+ sudo -u "$USER_GRAPHICAL" env DISPLAY="$DISPLAY" \
152
+ XDG_RUNTIME_DIR="/run/user/${ID}" \
153
+ DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${ID}/bus" \
154
+ PATH="$PATH" \
155
+ `;
156
+
134
157
  if (options.single === true) {
135
158
  // Run as a single session process
136
- shellExec(`setsid gnome-terminal -- bash -ic "${cmd}; exec bash" >/dev/null 2>&1 &`);
159
+ shellExec(`${confCmd} setsid gnome-terminal -- bash -ic '${cmd}; exec bash' >/dev/null 2>&1 &`, {
160
+ async: true,
161
+ });
137
162
  return;
138
163
  }
139
164
  // Run asynchronously and disown
140
- shellExec(`gnome-terminal -- bash -c "${cmd}; exec bash" & disown`, {
165
+ shellExec(`${confCmd} gnome-terminal -- bash -c '${cmd}; exec bash' >/dev/null 2>&1 & disown`, {
141
166
  async: true,
142
167
  stdout: true,
143
168
  });
@@ -13,10 +13,8 @@ import { createProxyMiddleware } from 'http-proxy-middleware';
13
13
  import { loggerFactory, loggerMiddleware } from './logger.js';
14
14
  import { buildPortProxyRouter, buildProxyRouter, getTlsHosts, isDevProxyContext, isTlsDevProxy } from './conf.js';
15
15
 
16
- import { SSL_BASE, TLS } from './tls.js';
17
16
  import { shellExec } from './process.js';
18
17
  import fs from 'fs-extra';
19
-
20
18
  import Underpost from '../index.js';
21
19
 
22
20
  dotenv.config();
@@ -85,7 +83,7 @@ class ProxyService {
85
83
  switch (port) {
86
84
  case 443:
87
85
  // For port 443 (HTTPS), create the SSL server
88
- const { ServerSSL } = await TLS.createSslServer(app, hosts);
86
+ const { ServerSSL } = await Underpost.tls.createSslServer(app, hosts);
89
87
  await Underpost.start.listenPortController(ServerSSL, port, runningData);
90
88
  break;
91
89
 
@@ -103,12 +101,12 @@ class ProxyService {
103
101
  if (isDevProxyContext() && isTlsDevProxy()) {
104
102
  tlsHosts = {};
105
103
  for (const tlsHost of getTlsHosts(hosts)) {
106
- if (fs.existsSync(SSL_BASE(tlsHost))) fs.removeSync(SSL_BASE(tlsHost));
107
- if (!TLS.validateSecureContext(tlsHost)) shellExec(`node bin/deploy tls "${tlsHost}"`);
104
+ if (fs.existsSync(Underpost.tls.SSL_BASE(tlsHost))) fs.removeSync(Underpost.tls.SSL_BASE(tlsHost));
105
+ if (!Underpost.tls.validateSecureContext(tlsHost)) shellExec(`node bin/deploy tls "${tlsHost}"`);
108
106
  tlsHosts[tlsHost] = {};
109
107
  }
110
108
  }
111
- const { ServerSSL } = await TLS.createSslServer(app, tlsHosts);
109
+ const { ServerSSL } = await Underpost.tls.createSslServer(app, tlsHosts);
112
110
  await Underpost.start.listenPortController(ServerSSL, port, runningData);
113
111
  break;
114
112
  }
package/src/server/tls.js CHANGED
@@ -1,8 +1,7 @@
1
1
  /**
2
- * Provides utilities for managing, building, and serving SSL/TLS contexts,
3
- * primarily using Certbot files and creating HTTPS servers.
4
- * @module src/server/tls.js
5
- * @namespace TransportLayerSecurity
2
+ * @class UnderpostTls
3
+ * @description TLS/SSL utilities for Underpost.
4
+ * @memberof UnderpostTls
6
5
  */
7
6
 
8
7
  import fs from 'fs-extra';
@@ -43,7 +42,7 @@ class TLS {
43
42
  * It attempts to be permissive: accepts cert-only, cert+ca, or fullchain.
44
43
  * @param {string} host
45
44
  * @returns {{key?:string, cert?:string, fullchain?:string, ca?:string, dir:string}}
46
- * @memberof TransportLayerSecurity
45
+ * @memberof UnderpostTls
47
46
  */
48
47
  static locateSslFiles(host = DEFAULT_HOST) {
49
48
  const dir = SSL_BASE(host);
@@ -103,7 +102,7 @@ class TLS {
103
102
  * Validate that a secure context can be built for host (key + cert or fullchain present)
104
103
  * @param {string} host
105
104
  * @returns {boolean}
106
- * @memberof TransportLayerSecurity
105
+ * @memberof UnderpostTls
107
106
  */
108
107
  static validateSecureContext(host = DEFAULT_HOST) {
109
108
  const files = TLS.locateSslFiles(host);
@@ -116,7 +115,7 @@ class TLS {
116
115
  * If separate cert + ca are found, they will be used accordingly.
117
116
  * @param {string} host
118
117
  * @returns {{key:string, cert:string, ca?:string}} options
119
- * @memberof TransportLayerSecurity
118
+ * @memberof UnderpostTls
120
119
  */
121
120
  static buildSecureContext(host = DEFAULT_HOST) {
122
121
  const files = TLS.locateSslFiles(host);
@@ -142,7 +141,7 @@ class TLS {
142
141
  * The function will copy existing discovered files to: key.key, crt.crt, ca_bundle.crt when possible.
143
142
  * @param {string} host
144
143
  * @returns {boolean} true if at least key+cert exist after operation
145
- * @memberof TransportLayerSecurity
144
+ * @memberof UnderpostTls
146
145
  */
147
146
  static async buildLocalSSL(host = DEFAULT_HOST) {
148
147
  const dir = SSL_BASE(host);
@@ -177,19 +176,16 @@ class TLS {
177
176
  * @param {import('express').Application} app
178
177
  * @param {Object<string, any>} hosts
179
178
  * @returns {{ServerSSL?: https.Server}}
180
- * @memberof TransportLayerSecurity
179
+ * @memberof UnderpostTls
181
180
  */
182
181
  static async createSslServer(app, hosts = { [DEFAULT_HOST]: {} }) {
183
182
  let server;
184
183
  for (const host of Object.keys(hosts)) {
185
184
  // ensure canonical files exist (copies where possible)
186
185
  await TLS.buildLocalSSL(host);
187
- if (!TLS.validate_secure_context_check(host)) {
188
- // backward compatibility: some callers expect validateSecureContext
189
- if (!TLS.validateSecureContext(host)) {
190
- logger.error('Invalid SSL context, skipping host', { host });
191
- continue;
192
- }
186
+ if (!TLS.validateSecureContext(host)) {
187
+ logger.error('Invalid SSL context, skipping host', { host });
188
+ continue;
193
189
  }
194
190
 
195
191
  // build secure context options
@@ -218,7 +214,7 @@ class TLS {
218
214
  * @param {number} port
219
215
  * @param {Object<string, any>} proxyRouter
220
216
  * @returns {import('express').RequestHandler}
221
- * @memberof TransportLayerSecurity
217
+ * @memberof UnderpostTls
222
218
  */
223
219
  static sslRedirectMiddleware(req, res, port = 80, proxyRouter = {}) {
224
220
  const sslRedirectUrl = `https://${req.headers.host}${req.url}`;
@@ -238,14 +234,23 @@ class TLS {
238
234
  }
239
235
  }
240
236
 
241
- // small helper for internal backward compatibility check name typo in older code
242
- TLS.validate_secure_context_check = TLS.validateSecureContext;
243
-
244
- // Backward compatibility exports
245
- const buildSSL = TLS.buildLocalSSL;
246
- const buildSecureContext = TLS.buildSecureContext;
247
- const validateSecureContext = TLS.validateSecureContext;
248
- const createSslServer = TLS.createSslServer;
249
- const sslRedirectMiddleware = TLS.sslRedirectMiddleware;
237
+ /**
238
+ * @class UnderpostTls
239
+ * @description TLS/SSL utilities for Underpost.
240
+ * @memberof UnderpostTls
241
+ */
242
+ class UnderpostTls {
243
+ static API = {
244
+ TLS,
245
+ SSL_BASE,
246
+ locateSslFiles: TLS.locateSslFiles,
247
+ validateSecureContext: TLS.validateSecureContext,
248
+ buildSecureContext: TLS.buildSecureContext,
249
+ buildLocalSSL: TLS.buildLocalSSL,
250
+ createSslServer: TLS.createSslServer,
251
+ sslRedirectMiddleware: TLS.sslRedirectMiddleware,
252
+ };
253
+ }
250
254
 
251
- export { TLS, SSL_BASE, buildSSL, buildSecureContext, validateSecureContext, createSslServer, sslRedirectMiddleware };
255
+ export { TLS, SSL_BASE, UnderpostTls };
256
+ export default UnderpostTls;
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- REMOTE_USER=$(node bin config get --plain DEFAULT_SSH_USER)
5
- REMOTE_HOST=$(node bin config get --plain DEFAULT_SSH_HOST)
6
- REMOTE_PORT=$(node bin config get --plain DEFAULT_SSH_PORT)
7
- SSH_KEY=$(node bin config get --plain DEFAULT_SSH_KEY_PATH)
8
-
9
- chmod 600 "$SSH_KEY"
10
-
11
- ssh -i "$SSH_KEY" -o BatchMode=yes "${REMOTE_USER}@${REMOTE_HOST}" -p ${REMOTE_PORT} sh <<EOF
12
- cd /home/dd/engine
13
- sudo -n -- /bin/bash -lc "node bin deploy dd production --status"
14
- sudo -n -- /bin/bash -lc "kubectl get pods -A"
15
- EOF