underpost 2.95.3 → 2.96.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +2 -2
  2. package/baremetal/commission-workflows.json +44 -0
  3. package/baremetal/packer-workflows.json +13 -0
  4. package/bin/deploy.js +6 -26
  5. package/cli.md +40 -43
  6. package/conf.js +4 -1
  7. package/examples/{QUICK-REFERENCE.md → static-page/QUICK-REFERENCE.md} +0 -18
  8. package/examples/{README.md → static-page/README.md} +3 -44
  9. package/examples/{STATIC-GENERATOR-GUIDE.md → static-page/STATIC-GENERATOR-GUIDE.md} +0 -50
  10. package/examples/{ssr-components → static-page/ssr-components}/CustomPage.js +0 -13
  11. package/examples/{static-config-simple.json → static-page/static-config-example.json} +1 -1
  12. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  13. package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
  14. package/package.json +1 -1
  15. package/packer/images/Rocky9Amd64/Makefile +62 -0
  16. package/packer/images/Rocky9Amd64/QUICKSTART.md +113 -0
  17. package/packer/images/Rocky9Amd64/README.md +122 -0
  18. package/packer/images/Rocky9Amd64/http/rocky9.ks.pkrtpl.hcl +114 -0
  19. package/packer/images/Rocky9Amd64/rocky9.pkr.hcl +160 -0
  20. package/packer/scripts/fuse-nbd +64 -0
  21. package/packer/scripts/fuse-tar-root +63 -0
  22. package/scripts/maas-setup.sh +13 -2
  23. package/scripts/maas-upload-boot-resource.sh +183 -0
  24. package/scripts/packer-init-vars-file.sh +30 -0
  25. package/scripts/packer-setup.sh +52 -0
  26. package/src/cli/baremetal.js +262 -65
  27. package/src/cli/cloud-init.js +11 -5
  28. package/src/cli/cron.js +161 -29
  29. package/src/cli/db.js +59 -92
  30. package/src/cli/env.js +24 -3
  31. package/src/cli/index.js +18 -58
  32. package/src/cli/repository.js +178 -0
  33. package/src/cli/run.js +2 -3
  34. package/src/cli/static.js +99 -194
  35. package/src/client/services/default/default.management.js +7 -0
  36. package/src/index.js +1 -1
  37. package/src/server/backup.js +4 -53
  38. package/src/server/conf.js +3 -4
  39. package/examples/static-config-example.json +0 -183
  40. package/src/client/ssr/pages/404.js +0 -12
  41. package/src/client/ssr/pages/500.js +0 -12
  42. package/src/client/ssr/pages/maintenance.js +0 -14
  43. package/src/client/ssr/pages/offline.js +0 -21
@@ -465,6 +465,13 @@ const DefaultManagement = {
465
465
  const selectedRows = AgGrid.grids[gridId].getSelectedRows();
466
466
  logger.info('selectedRows', selectedRows);
467
467
  },
468
+ onRowEditingStarted: async (...args) => {
469
+ // Show only save button when editing starts
470
+ s(`.management-table-btn-save-${id}`).classList.remove('hide');
471
+ if (permissions.add) s(`.management-table-btn-add-${id}`).classList.add('hide');
472
+ if (permissions.remove) s(`.management-table-btn-clean-${id}`).classList.add('hide');
473
+ if (permissions.reload) s(`.management-table-btn-reload-${id}`).classList.add('hide');
474
+ },
468
475
  onRowValueChanged: async (...args) => {
469
476
  const [event] = args;
470
477
  logger.info('onRowValueChanged', args);
package/src/index.js CHANGED
@@ -36,7 +36,7 @@ class Underpost {
36
36
  * @type {String}
37
37
  * @memberof Underpost
38
38
  */
39
- static version = 'v2.95.3';
39
+ static version = 'v2.96.0';
40
40
  /**
41
41
  * Repository cli API
42
42
  * @static
@@ -7,7 +7,6 @@
7
7
  import fs from 'fs-extra';
8
8
  import { loggerFactory } from './logger.js';
9
9
  import { shellExec } from './process.js';
10
- import { getCronBackUpFolder } from './conf.js';
11
10
  import dotenv from 'dotenv';
12
11
 
13
12
  dotenv.config();
@@ -25,70 +24,22 @@ class BackUp {
25
24
  * @description Initiates a backup operation for the specified deployment list.
26
25
  * @param {string} deployList - The list of deployments to backup.
27
26
  * @param {Object} options - The options for the backup operation.
28
- * @param {boolean} options.itc - Whether to backup inside container data.
29
27
  * @param {boolean} options.git - Whether to backup data using Git.
30
28
  * @memberof BackUp
31
29
  */
32
- static callback = async function (deployList, options = { itc: false, git: false }) {
30
+ static callback = async function (deployList, options = { git: false }) {
33
31
  if ((!deployList || deployList === 'dd') && fs.existsSync(`./engine-private/deploy/dd.router`))
34
- deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
32
+ deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').trim();
35
33
 
36
34
  logger.info('init backups callback', deployList);
37
35
  await logger.setUpInfo();
38
- const currentDate = new Date().getTime();
39
- const maxBackupRetention = 5;
40
-
41
- if (!fs.existsSync('./engine-private/cron-backups'))
42
- fs.mkdirSync('./engine-private/cron-backups', { recursive: true });
43
36
 
44
37
  for (const _deployId of deployList.split(',')) {
45
38
  const deployId = _deployId.trim();
46
39
  if (!deployId) continue;
47
40
 
48
- if (!(options.itc === true)) {
49
- shellExec(`node bin db ${options.git ? '--git ' : ''}--export ${deployId}`);
50
- continue;
51
- }
52
-
53
- const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
54
-
55
- for (const host of Object.keys(confServer))
56
- for (const path of Object.keys(confServer[host])) {
57
- // retention policy
58
- const { db } = confServer[host][path];
59
- if (!db) continue;
60
- logger.info('Init backup', { host, path, db });
61
-
62
- const backUpPath = `${process.cwd()}/engine-private/cron-backups/${getCronBackUpFolder(host, path)}`;
63
- if (!fs.existsSync(backUpPath)) fs.mkdirSync(`${backUpPath}`, { recursive: true });
64
- // .isDirectory()
65
- const files = await fs.readdir(backUpPath, { withFileTypes: true });
66
-
67
- const currentBackupsDirs = files
68
- .map((fileObj) => parseInt(fileObj.name))
69
- .sort((a, b) => a - b)
70
- .reverse();
71
-
72
- for (const retentionPath of currentBackupsDirs.filter((t, i) => i >= maxBackupRetention - 1)) {
73
- const removePathRetention = `${backUpPath}/${retentionPath}`;
74
- logger.info('Remove backup folder', removePathRetention);
75
- fs.removeSync(removePathRetention);
76
- }
77
-
78
- fs.mkdirSync(`${backUpPath}/${currentDate}`, { recursive: true });
79
-
80
- shellExec(`node bin/db ${host}${path} export ${deployId} ${backUpPath}/${currentDate}`);
81
- }
82
- shellExec(
83
- `cd ./engine-private/cron-backups` +
84
- ` && underpost pull . ${process.env.GITHUB_USERNAME}/cron-backups` +
85
- ` && git add .` +
86
- ` && underpost cmt . backup cron-job '${new Date().toLocaleDateString()}'` +
87
- ` && underpost push . ${process.env.GITHUB_USERNAME}/cron-backups`,
88
- {
89
- silent: true,
90
- },
91
- );
41
+ logger.info('Executing database export for', deployId);
42
+ shellExec(`node bin db ${options.git ? '--git ' : ''}--export ${deployId}`);
92
43
  }
93
44
  };
94
45
  }
@@ -1227,13 +1227,12 @@ const Cmd = {
1227
1227
  * @param {string} name - The name.
1228
1228
  * @param {string} expression - The expression.
1229
1229
  * @param {object} options - The options.
1230
+ * @param {number} instances - The number of PM2 instances (default: 1).
1230
1231
  * @returns {string} - The cron command.
1231
1232
  * @memberof Cmd
1232
1233
  */
1233
- cron: (deployList, jobList, name, expression, options) =>
1234
- `pm2 start ./bin/index.js --no-autorestart --instances 1 --cron "${expression}" --name ${name} -- cron ${
1235
- options?.itc ? `--itc ` : ''
1236
- }${options?.git ? `--git ` : ''}${deployList} ${jobList}`,
1234
+ cron: (deployList, jobList, name, expression, options, instances = 1) =>
1235
+ `pm2 start ./bin/index.js --no-autorestart --instances ${instances} --cron "${expression}" --name ${name} -- cron ${options?.git ? `--git ` : ''}${deployList} ${jobList}`,
1237
1236
  };
1238
1237
 
1239
1238
  /**
@@ -1,183 +0,0 @@
1
- {
2
- "_comment": "Example static site configuration file for Underpost Static Generator",
3
- "_usage": "Use with: underpost static --config-file ./static-config-example.json",
4
-
5
- "page": "./src/client/ssr/body/DefaultSplashScreen.js",
6
- "outputPath": "./dist/index.html",
7
- "env": "production",
8
- "minify": true,
9
- "lang": "en",
10
- "dir": "ltr",
11
-
12
- "metadata": {
13
- "_comment": "SEO and social media metadata",
14
- "title": "My Awesome Application",
15
- "description": "A comprehensive example of a static site with full metadata customization",
16
- "keywords": ["static site", "generator", "underpost", "seo", "progressive web app"],
17
- "author": "Your Name or Company",
18
- "themeColor": "#4CAF50",
19
- "canonicalURL": "https://example.com",
20
- "thumbnail": "https://example.com/images/og-thumbnail.png",
21
- "locale": "en-US",
22
- "siteName": "My Awesome Site",
23
- "openGraph": {
24
- "_comment": "Additional Open Graph properties",
25
- "type": "website",
26
- "image:width": "1200",
27
- "image:height": "630"
28
- },
29
- "twitter": {
30
- "_comment": "Twitter card specific metadata",
31
- "site": "@yourhandle",
32
- "creator": "@creatorhandle"
33
- }
34
- },
35
-
36
- "scripts": {
37
- "_comment": "Scripts to inject into head and body sections",
38
- "head": [
39
- {
40
- "_comment": "External analytics script with async loading",
41
- "src": "https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID",
42
- "async": true
43
- },
44
- {
45
- "_comment": "Inline configuration script",
46
- "content": "window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'GA_MEASUREMENT_ID');",
47
- "type": "text/javascript"
48
- },
49
- {
50
- "_comment": "App configuration as inline script",
51
- "content": "window.appConfig = { apiUrl: 'https://api.example.com', version: '1.0.0', features: { analytics: true, debugging: false } };",
52
- "type": "text/javascript"
53
- }
54
- ],
55
- "body": [
56
- {
57
- "_comment": "Main application bundle as ES module",
58
- "src": "/dist/app.js",
59
- "type": "module",
60
- "defer": true
61
- },
62
- {
63
- "_comment": "Service worker registration",
64
- "content": "if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js').then(reg => console.log('SW registered', reg)).catch(err => console.log('SW error', err)); }",
65
- "type": "text/javascript"
66
- }
67
- ]
68
- },
69
-
70
- "styles": [
71
- {
72
- "_comment": "Main stylesheet",
73
- "href": "/styles/main.css"
74
- },
75
- {
76
- "_comment": "External font",
77
- "href": "https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap"
78
- },
79
- {
80
- "_comment": "Critical inline CSS for faster rendering",
81
- "content": "body { margin: 0; padding: 0; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; } * { box-sizing: border-box; }"
82
- },
83
- {
84
- "_comment": "Print stylesheet",
85
- "href": "/styles/print.css",
86
- "media": "print"
87
- }
88
- ],
89
-
90
- "icons": {
91
- "_comment": "Icon configuration for various platforms",
92
- "favicon": "/favicon.ico",
93
- "appleTouchIcon": "/apple-touch-icon.png",
94
- "manifest": "/manifest.json",
95
- "additional": [
96
- {
97
- "rel": "icon",
98
- "type": "image/png",
99
- "sizes": "32x32",
100
- "href": "/favicon-32x32.png"
101
- },
102
- {
103
- "rel": "icon",
104
- "type": "image/png",
105
- "sizes": "16x16",
106
- "href": "/favicon-16x16.png"
107
- },
108
- {
109
- "rel": "mask-icon",
110
- "href": "/safari-pinned-tab.svg",
111
- "color": "#4CAF50"
112
- }
113
- ]
114
- },
115
-
116
- "_headComponents_comment": "SSR components disabled in this example - they require specific data structures",
117
- "headComponents": [],
118
-
119
- "_bodyComponents_comment": "Additional SSR components to include in body section",
120
- "bodyComponents": [],
121
-
122
- "microdata": [
123
- {
124
- "_comment": "Organization schema for better SEO",
125
- "@context": "https://schema.org",
126
- "@type": "Organization",
127
- "name": "My Organization",
128
- "url": "https://example.com",
129
- "logo": "https://example.com/logo.png",
130
- "contactPoint": {
131
- "@type": "ContactPoint",
132
- "telephone": "+1-555-123-4567",
133
- "contactType": "Customer Service"
134
- },
135
- "sameAs": [
136
- "https://twitter.com/yourhandle",
137
- "https://linkedin.com/company/yourcompany",
138
- "https://github.com/yourorg"
139
- ]
140
- },
141
- {
142
- "_comment": "WebSite schema",
143
- "@context": "https://schema.org",
144
- "@type": "WebSite",
145
- "name": "My Awesome Site",
146
- "url": "https://example.com",
147
- "potentialAction": {
148
- "@type": "SearchAction",
149
- "target": "https://example.com/search?q={search_term_string}",
150
- "query-input": "required name=search_term_string"
151
- }
152
- },
153
- {
154
- "_comment": "WebPage schema",
155
- "@context": "https://schema.org",
156
- "@type": "WebPage",
157
- "name": "Home Page",
158
- "description": "Welcome to our awesome site",
159
- "url": "https://example.com"
160
- }
161
- ],
162
-
163
- "customPayload": {
164
- "_comment": "Custom data injected into window.renderPayload",
165
- "apiEndpoint": "https://api.example.com",
166
- "cdnUrl": "https://cdn.example.com",
167
- "features": {
168
- "chat": true,
169
- "notifications": true,
170
- "darkMode": true
171
- },
172
- "externalServices": {
173
- "analytics": "GA_MEASUREMENT_ID",
174
- "maps": "GOOGLE_MAPS_API_KEY"
175
- }
176
- },
177
-
178
- "buildPath": "/",
179
- "deployId": "",
180
- "buildHost": "",
181
- "build": false,
182
- "dev": false
183
- }
@@ -1,12 +0,0 @@
1
- import { e404 } from '../common/Alert.js';
2
- import { append } from '../common/SsrCore.js';
3
- import { Translate } from '../common/Translate.js';
4
- import { Worker } from '../common/Worker.js';
5
- /*imports*/
6
-
7
- window.onload = () =>
8
- Worker.instance({
9
- render: async () => {
10
- append('.page-render', await e404({ Translate }));
11
- },
12
- });
@@ -1,12 +0,0 @@
1
- import { e500 } from '../common/Alert.js';
2
- import { append } from '../common/SsrCore.js';
3
- import { Translate } from '../common/Translate.js';
4
- import { Worker } from '../common/Worker.js';
5
- /*imports*/
6
-
7
- window.onload = () =>
8
- Worker.instance({
9
- render: async () => {
10
- append('.page-render', await e500({ Translate }));
11
- },
12
- });
@@ -1,14 +0,0 @@
1
- import { htmls, loggerFactory } from '../common/SsrCore.js';
2
- import { Alert } from '../common/Alert.js';
3
- import { Translate } from '../common/Translate.js';
4
- import { Worker } from '../common/Worker.js';
5
- /*imports*/
6
-
7
- const logger = loggerFactory({ url: location.toString() });
8
-
9
- window.onload = () =>
10
- Worker.instance({
11
- render: async () => {
12
- htmls(`.page-render`, html`${await Alert.maintenance({ Translate })}`);
13
- },
14
- });
@@ -1,21 +0,0 @@
1
- import { htmls, loggerFactory } from '../common/SsrCore.js';
2
- import { Alert } from '../common/Alert.js';
3
- import { Translate } from '../common/Translate.js';
4
- import { Worker } from '../common/Worker.js';
5
- /*imports*/
6
-
7
- const logger = loggerFactory({ url: location.toString() });
8
-
9
- window.onload = () =>
10
- Worker.instance({
11
- render: async () => {
12
- window.ononline = async () => {
13
- location.href = location.pathname.split('/')[1] ? `/${location.pathname.split('/')[1].split('.')[0]}` : '/';
14
- };
15
- window.onoffline = async () => {
16
- htmls(`.page-render`, html`${await Alert.noInternet({ Translate })}`);
17
- };
18
- if (navigator.onLine && !location.hostname.match('localhost')) window.ononline();
19
- else window.onoffline();
20
- },
21
- });