underpost 3.1.2 → 3.2.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 (98) hide show
  1. package/.env.example +0 -2
  2. package/.github/workflows/ghpkg.ci.yml +4 -4
  3. package/.github/workflows/npmpkg.ci.yml +38 -7
  4. package/.github/workflows/pwa-microservices-template-page.cd.yml +3 -4
  5. package/.github/workflows/pwa-microservices-template-test.ci.yml +3 -3
  6. package/.github/workflows/release.cd.yml +4 -4
  7. package/CHANGELOG.md +365 -1
  8. package/CLI-HELP.md +55 -3
  9. package/README.md +7 -3
  10. package/bin/build.js +18 -12
  11. package/bin/deploy.js +205 -225
  12. package/bin/file.js +3 -0
  13. package/conf.js +4 -10
  14. package/jsdoc.json +1 -1
  15. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  16. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  17. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  18. package/manifests/deployment/dd-test-development/deployment.yaml +72 -50
  19. package/manifests/deployment/dd-test-development/proxy.yaml +13 -4
  20. package/manifests/deployment/playwright/deployment.yaml +1 -1
  21. package/nodemon.json +1 -1
  22. package/package.json +21 -14
  23. package/scripts/ports-ls.sh +2 -0
  24. package/scripts/rhel-grpc-setup.sh +56 -0
  25. package/src/api/file/file.ref.json +18 -0
  26. package/src/api/user/user.service.js +8 -7
  27. package/src/cli/cluster.js +7 -7
  28. package/src/cli/db.js +76 -242
  29. package/src/cli/deploy.js +104 -65
  30. package/src/cli/env.js +1 -0
  31. package/src/cli/fs.js +2 -1
  32. package/src/cli/index.js +50 -1
  33. package/src/cli/kubectl.js +211 -0
  34. package/src/cli/release.js +284 -0
  35. package/src/cli/repository.js +328 -112
  36. package/src/cli/run.js +283 -69
  37. package/src/cli/test.js +3 -3
  38. package/src/client/Default.index.js +3 -4
  39. package/src/client/components/core/Alert.js +2 -2
  40. package/src/client/components/core/AppStore.js +69 -0
  41. package/src/client/components/core/CalendarCore.js +2 -2
  42. package/src/client/components/core/Docs.js +9 -2
  43. package/src/client/components/core/DropDown.js +129 -17
  44. package/src/client/components/core/Keyboard.js +2 -2
  45. package/src/client/components/core/LogIn.js +2 -2
  46. package/src/client/components/core/LogOut.js +2 -2
  47. package/src/client/components/core/Modal.js +0 -1
  48. package/src/client/components/core/Panel.js +0 -1
  49. package/src/client/components/core/PanelForm.js +19 -19
  50. package/src/client/components/core/RichText.js +1 -2
  51. package/src/client/components/core/SocketIo.js +82 -29
  52. package/src/client/components/core/SocketIoHandler.js +75 -0
  53. package/src/client/components/core/Stream.js +143 -95
  54. package/src/client/components/core/Webhook.js +40 -7
  55. package/src/client/components/default/AppStoreDefault.js +5 -0
  56. package/src/client/components/default/LogInDefault.js +3 -3
  57. package/src/client/components/default/LogOutDefault.js +2 -2
  58. package/src/client/components/default/MenuDefault.js +5 -5
  59. package/src/client/components/default/SocketIoDefault.js +3 -51
  60. package/src/client/services/core/core.service.js +20 -8
  61. package/src/client/services/user/user.management.js +2 -2
  62. package/src/client/ssr/body/404.js +15 -11
  63. package/src/client/ssr/body/500.js +15 -11
  64. package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
  65. package/src/client/ssr/offline/NoNetworkConnection.js +11 -10
  66. package/src/client/ssr/pages/Test.js +11 -10
  67. package/src/index.js +24 -1
  68. package/src/runtime/express/Express.js +26 -9
  69. package/src/runtime/lampp/Dockerfile +9 -2
  70. package/src/runtime/lampp/Lampp.js +4 -3
  71. package/src/runtime/wp/Dockerfile +64 -0
  72. package/src/runtime/wp/Wp.js +497 -0
  73. package/src/server/auth.js +30 -6
  74. package/src/server/backup.js +19 -1
  75. package/src/server/client-build-docs.js +51 -110
  76. package/src/server/client-build.js +55 -64
  77. package/src/server/client-formatted.js +109 -57
  78. package/src/server/conf.js +19 -15
  79. package/src/server/ipfs-client.js +24 -1
  80. package/src/server/peer.js +8 -0
  81. package/src/server/runtime.js +25 -1
  82. package/src/server/start.js +21 -8
  83. package/src/ws/IoInterface.js +1 -10
  84. package/src/ws/IoServer.js +14 -33
  85. package/src/ws/core/channels/core.ws.chat.js +65 -20
  86. package/src/ws/core/channels/core.ws.mailer.js +113 -32
  87. package/src/ws/core/channels/core.ws.stream.js +90 -31
  88. package/src/ws/core/core.ws.connection.js +12 -33
  89. package/src/ws/core/core.ws.emit.js +10 -26
  90. package/src/ws/core/core.ws.server.js +25 -58
  91. package/src/ws/default/channels/default.ws.main.js +53 -12
  92. package/src/ws/default/default.ws.connection.js +26 -13
  93. package/src/ws/default/default.ws.server.js +30 -12
  94. package/src/client/components/default/ElementsDefault.js +0 -38
  95. package/src/ws/core/management/core.ws.chat.js +0 -8
  96. package/src/ws/core/management/core.ws.mailer.js +0 -16
  97. package/src/ws/core/management/core.ws.stream.js +0 -8
  98. package/src/ws/default/management/default.ws.main.js +0 -8
@@ -46,9 +46,12 @@ const main = () => {
46
46
  a {
47
47
  color: black;
48
48
  }
49
+ .main-body-ssr-500 {
50
+ top: 45%;
51
+ }
49
52
  </style>
50
53
 
51
- <div class="abs center" style="top: 45%">
54
+ <div class="abs center main-body-ssr-500">
52
55
  ${icon}
53
56
  <br />
54
57
  <br />
@@ -56,17 +59,18 @@ const main = () => {
56
59
  <br />
57
60
  <br />${Translate.Render('page-broken')} <br />
58
61
  <br />
59
- <a href="${location.origin}">${Translate.Render('back')}</a>
62
+ <a target="_top" href="${location.origin}">${Translate.Render('back')}</a>
60
63
  </div>`,
61
64
  );
62
65
  };
63
66
 
64
- SrrComponent = () => html`<script>
65
- {
66
- const s = ${s};
67
- const append = ${append};
68
- const getLang = ${getLang};
69
- const main = ${main};
70
- window.onload = main;
71
- }
72
- </script>`;
67
+ SrrComponent = () =>
68
+ html`<script>
69
+ {
70
+ const s = ${s};
71
+ const append = ${append};
72
+ const getLang = ${getLang};
73
+ const main = ${main};
74
+ window.onload = main;
75
+ }
76
+ </script>`;
@@ -0,0 +1,285 @@
1
+ const swaggerDarkCss = css`
2
+ /* ── Toggle button ── */
3
+ .swagger-ui .topbar-wrapper {
4
+ display: flex;
5
+ align-items: center;
6
+ }
7
+ .swagger-theme-toggle-btn {
8
+ background: rgba(255, 255, 255, 0.12);
9
+ border: 1px solid rgba(255, 255, 255, 0.3);
10
+ color: #fff;
11
+ padding: 5px 14px;
12
+ border-radius: 4px;
13
+ cursor: pointer;
14
+ font-size: 13px;
15
+ margin-left: auto;
16
+ margin-right: 16px;
17
+ white-space: nowrap;
18
+ transition: background 0.2s ease;
19
+ }
20
+ .swagger-theme-toggle-btn:hover {
21
+ background: rgba(255, 255, 255, 0.25);
22
+ }
23
+
24
+ /* ── Dark mode overrides — black/gray gradients ── */
25
+ body.swagger-dark {
26
+ background: #0f0f0f;
27
+ }
28
+ body.swagger-dark .swagger-ui {
29
+ background: #0f0f0f;
30
+ }
31
+ body.swagger-dark .swagger-ui .wrapper {
32
+ background: #0f0f0f;
33
+ }
34
+
35
+ /* Info block */
36
+ body.swagger-dark .swagger-ui .info .title,
37
+ body.swagger-dark .swagger-ui .info h1,
38
+ body.swagger-dark .swagger-ui .info h2,
39
+ body.swagger-dark .swagger-ui .info h3,
40
+ body.swagger-dark .swagger-ui .info p,
41
+ body.swagger-dark .swagger-ui .info li,
42
+ body.swagger-dark .swagger-ui .info a {
43
+ color: #e8e8e8;
44
+ }
45
+
46
+ /* Scheme / server selector bar */
47
+ body.swagger-dark .swagger-ui .scheme-container {
48
+ background: linear-gradient(180deg, #1a1a1a 0%, #222222 100%);
49
+ box-shadow: 0 1px 0 #383838;
50
+ }
51
+ body.swagger-dark .swagger-ui select {
52
+ background: #2a2a2a;
53
+ color: #e8e8e8;
54
+ border-color: #383838;
55
+ }
56
+
57
+ /* Operation tags */
58
+ body.swagger-dark .swagger-ui .opblock-tag {
59
+ color: #e8e8e8;
60
+ border-color: #383838;
61
+ }
62
+ body.swagger-dark .swagger-ui .opblock-tag small {
63
+ color: #a8a8a8;
64
+ }
65
+ body.swagger-dark .swagger-ui .opblock-tag:hover {
66
+ background: rgba(255, 255, 255, 0.04);
67
+ }
68
+
69
+ /* Operation blocks */
70
+ body.swagger-dark .swagger-ui .opblock {
71
+ background: linear-gradient(180deg, #161616 0%, #1e1e1e 100%);
72
+ border-color: #383838;
73
+ }
74
+ body.swagger-dark .swagger-ui .opblock .opblock-summary {
75
+ border-color: #383838;
76
+ }
77
+ body.swagger-dark .swagger-ui .opblock .opblock-summary-description,
78
+ body.swagger-dark .swagger-ui .opblock .opblock-summary-operation-id {
79
+ color: #a8a8a8;
80
+ }
81
+ body.swagger-dark .swagger-ui .opblock-body {
82
+ background: #0f0f0f;
83
+ }
84
+ body.swagger-dark .swagger-ui .opblock-description-wrapper p,
85
+ body.swagger-dark .swagger-ui .opblock-external-docs-wrapper p {
86
+ color: #a8a8a8;
87
+ }
88
+ body.swagger-dark .swagger-ui .opblock-section-header {
89
+ background: linear-gradient(180deg, #1a1a1a 0%, #222222 100%);
90
+ border-color: #383838;
91
+ }
92
+ body.swagger-dark .swagger-ui .opblock-section-header h4 {
93
+ color: #e8e8e8;
94
+ }
95
+
96
+ /* Models section */
97
+ body.swagger-dark .swagger-ui section.models {
98
+ background: linear-gradient(180deg, #161616 0%, #1e1e1e 100%);
99
+ border-color: #383838;
100
+ }
101
+ body.swagger-dark .swagger-ui section.models h4,
102
+ body.swagger-dark .swagger-ui section.models h5 {
103
+ color: #e8e8e8;
104
+ border-color: #383838;
105
+ }
106
+ body.swagger-dark .swagger-ui .model-container {
107
+ background: #0f0f0f;
108
+ border-color: #2a2a2a;
109
+ }
110
+ body.swagger-dark .swagger-ui .model-box {
111
+ background: #1a1a1a;
112
+ }
113
+ body.swagger-dark .swagger-ui .model,
114
+ body.swagger-dark .swagger-ui .model-title,
115
+ body.swagger-dark .swagger-ui .model span,
116
+ body.swagger-dark .swagger-ui .model .property {
117
+ color: #e8e8e8;
118
+ }
119
+ body.swagger-dark .swagger-ui .model .property.primitive {
120
+ color: #a8a8a8;
121
+ }
122
+
123
+ /* Tables */
124
+ body.swagger-dark .swagger-ui table thead tr th {
125
+ background: linear-gradient(180deg, #222222 0%, #2a2a2a 100%);
126
+ color: #e8e8e8;
127
+ border-color: #383838;
128
+ }
129
+ body.swagger-dark .swagger-ui table tbody tr td {
130
+ color: #a8a8a8;
131
+ border-color: #2a2a2a;
132
+ }
133
+
134
+ /* Parameters */
135
+ body.swagger-dark .swagger-ui .parameter__name,
136
+ body.swagger-dark .swagger-ui .parameter__type,
137
+ body.swagger-dark .swagger-ui .parameter__in,
138
+ body.swagger-dark .swagger-ui .parameter__extension,
139
+ body.swagger-dark .swagger-ui .parameter__empty_value_toggle {
140
+ color: #a8a8a8;
141
+ }
142
+ body.swagger-dark .swagger-ui .parameter__name.required:after {
143
+ color: #ff6b6b;
144
+ }
145
+
146
+ /* Inputs & textareas */
147
+ body.swagger-dark .swagger-ui input[type='text'],
148
+ body.swagger-dark .swagger-ui input[type='email'],
149
+ body.swagger-dark .swagger-ui input[type='password'],
150
+ body.swagger-dark .swagger-ui input[type='search'],
151
+ body.swagger-dark .swagger-ui input[type='number'],
152
+ body.swagger-dark .swagger-ui textarea {
153
+ background: #2a2a2a;
154
+ color: #e8e8e8;
155
+ border-color: #383838;
156
+ }
157
+
158
+ /* Buttons */
159
+ body.swagger-dark .swagger-ui .btn {
160
+ color: #e8e8e8;
161
+ border-color: #383838;
162
+ background: #222222;
163
+ }
164
+ body.swagger-dark .swagger-ui .btn:hover {
165
+ background: #2a2a2a;
166
+ }
167
+ body.swagger-dark .swagger-ui .btn.authorize {
168
+ color: #49cc90;
169
+ border-color: #49cc90;
170
+ background: transparent;
171
+ }
172
+ body.swagger-dark .swagger-ui .btn.execute {
173
+ background: linear-gradient(180deg, #333333 0%, #262626 100%);
174
+ border-color: #484848;
175
+ color: #e8e8e8;
176
+ }
177
+ body.swagger-dark .swagger-ui .btn.cancel {
178
+ color: #ff6b6b;
179
+ border-color: #ff6b6b;
180
+ }
181
+
182
+ /* Responses */
183
+ body.swagger-dark .swagger-ui .responses-inner h4,
184
+ body.swagger-dark .swagger-ui .responses-inner h5 {
185
+ color: #a8a8a8;
186
+ }
187
+ body.swagger-dark .swagger-ui .response-col_status {
188
+ color: #e8e8e8;
189
+ }
190
+ body.swagger-dark .swagger-ui .response-col_description__inner p {
191
+ color: #a8a8a8;
192
+ }
193
+ body.swagger-dark .swagger-ui .response {
194
+ border-color: #2a2a2a;
195
+ }
196
+
197
+ /* Code / highlight */
198
+ body.swagger-dark .swagger-ui .highlight-code,
199
+ body.swagger-dark .swagger-ui .microlight {
200
+ background: #1a1a1a;
201
+ border: 1px solid #2a2a2a;
202
+ }
203
+
204
+ /* Markdown */
205
+ body.swagger-dark .swagger-ui .markdown p,
206
+ body.swagger-dark .swagger-ui .markdown li,
207
+ body.swagger-dark .swagger-ui .markdown a {
208
+ color: #a8a8a8;
209
+ }
210
+
211
+ /* Tabs */
212
+ body.swagger-dark .swagger-ui .tab li {
213
+ color: #a8a8a8;
214
+ }
215
+ body.swagger-dark .swagger-ui .tab li.active {
216
+ color: #e8e8e8;
217
+ }
218
+
219
+ /* Auth / modal dialog */
220
+ body.swagger-dark .swagger-ui .dialog-ux .modal-ux {
221
+ background: linear-gradient(180deg, #1a1a1a 0%, #222222 100%);
222
+ border: 1px solid #383838;
223
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.7);
224
+ }
225
+ body.swagger-dark .swagger-ui .dialog-ux .modal-ux-header {
226
+ border-bottom: 1px solid #383838;
227
+ background: linear-gradient(180deg, #222222 0%, #1a1a1a 100%);
228
+ }
229
+ body.swagger-dark .swagger-ui .dialog-ux .modal-ux-header h3 {
230
+ color: #e8e8e8;
231
+ }
232
+ body.swagger-dark .swagger-ui .dialog-ux .modal-ux-content p,
233
+ body.swagger-dark .swagger-ui .dialog-ux .modal-ux-content label,
234
+ body.swagger-dark .swagger-ui .dialog-ux .modal-ux-content h4 {
235
+ color: #e8e8e8;
236
+ }
237
+ body.swagger-dark .swagger-ui .auth-container .errors {
238
+ color: #ff6b6b;
239
+ }
240
+
241
+ /* Info / description */
242
+ body.swagger-dark .swagger-ui .info .base-url,
243
+ body.swagger-dark .swagger-ui .servers-title,
244
+ body.swagger-dark .swagger-ui .servers > label {
245
+ color: #a8a8a8;
246
+ }
247
+
248
+ /* Expand/collapse arrows */
249
+ body.swagger-dark .swagger-ui svg.arrow path {
250
+ fill: #a8a8a8;
251
+ }
252
+ `;
253
+
254
+ const swaggerDarkJs = `(function () {
255
+ function injectThemeToggle() {
256
+ var topbarWrapper = document.querySelector('.swagger-ui .topbar-wrapper');
257
+ if (!topbarWrapper || document.getElementById('swagger-theme-toggle')) return;
258
+
259
+ var savedTheme = localStorage.getItem('swagger-theme') || 'light';
260
+ if (savedTheme === 'dark') document.body.classList.add('swagger-dark');
261
+
262
+ var btn = document.createElement('button');
263
+ btn.id = 'swagger-theme-toggle';
264
+ btn.className = 'swagger-theme-toggle-btn';
265
+ btn.setAttribute('title', 'Toggle dark / light mode');
266
+ btn.textContent = savedTheme === 'dark' ? '\u2600\uFE0F Light Mode' : '\uD83C\uDF19 Dark Mode';
267
+
268
+ btn.addEventListener('click', function () {
269
+ var isDark = document.body.classList.toggle('swagger-dark');
270
+ localStorage.setItem('swagger-theme', isDark ? 'dark' : 'light');
271
+ btn.textContent = isDark ? '\u2600\uFE0F Light Mode' : '\uD83C\uDF19 Dark Mode';
272
+ });
273
+
274
+ topbarWrapper.appendChild(btn);
275
+ }
276
+
277
+ var poll = setInterval(function () {
278
+ if (document.querySelector('.swagger-ui .topbar-wrapper')) {
279
+ injectThemeToggle();
280
+ clearInterval(poll);
281
+ }
282
+ }, 100);
283
+ })();`;
284
+
285
+ SrrComponent = () => ({ css: swaggerDarkCss, js: swaggerDarkJs });
@@ -51,17 +51,18 @@ const main = () => {
51
51
  <br />
52
52
  <br />${Translate.Render('no-internet-connection')} <br />
53
53
  <br />
54
- <a href="${location.origin}">${Translate.Render('back')}</a>
54
+ <a target="_top" href="${location.origin}">${Translate.Render('back')}</a>
55
55
  </div>`,
56
56
  );
57
57
  };
58
58
 
59
- SrrComponent = () => html`<script>
60
- {
61
- const s = ${s};
62
- const append = ${append};
63
- const getLang = ${getLang};
64
- const main = ${main};
65
- window.onload = main;
66
- }
67
- </script>`;
59
+ SrrComponent = () =>
60
+ html`<script>
61
+ {
62
+ const s = ${s};
63
+ const append = ${append};
64
+ const getLang = ${getLang};
65
+ const main = ${main};
66
+ window.onload = main;
67
+ }
68
+ </script>`;
@@ -182,17 +182,18 @@ const main = () => {
182
182
  <span class="bold">Test Page</span>
183
183
  <br />
184
184
  <br />
185
- <a href="${location.origin}">${Translate.Render('back')}</a>
185
+ <a target="_top" href="${location.origin}">${Translate.Render('back')}</a>
186
186
  </div>`,
187
187
  );
188
188
  };
189
189
 
190
- SrrComponent = () => html`<script>
191
- {
192
- const s = ${s};
193
- const append = ${append};
194
- const getLang = ${getLang};
195
- const main = ${main};
196
- window.onload = main;
197
- }
198
- </script>`;
190
+ SrrComponent = () =>
191
+ html`<script>
192
+ {
193
+ const s = ${s};
194
+ const append = ${append};
195
+ const getLang = ${getLang};
196
+ const main = ${main};
197
+ window.onload = main;
198
+ }
199
+ </script>`;
package/src/index.js CHANGED
@@ -10,6 +10,7 @@ import UnderpostKickStart from './cli/kickstart.js';
10
10
  import UnderpostCluster from './cli/cluster.js';
11
11
  import UnderpostDB from './cli/db.js';
12
12
  import UnderpostDeploy from './cli/deploy.js';
13
+ import UnderpostKubectl from './cli/kubectl.js';
13
14
  import UnderpostRootEnv from './cli/env.js';
14
15
  import UnderpostFileStorage from './cli/fs.js';
15
16
  import UnderpostIPFS from './cli/ipfs.js';
@@ -22,6 +23,7 @@ import UnderpostSecret from './cli/secrets.js';
22
23
  import UnderpostSSH from './cli/ssh.js';
23
24
  import UnderpostStatic from './cli/static.js';
24
25
  import UnderpostTest from './cli/test.js';
26
+ import UnderpostRelease from './cli/release.js';
25
27
  import UnderpostSystemProvisionig from './cli/system.js';
26
28
 
27
29
  import UnderpostDns from './server/dns.js';
@@ -42,7 +44,7 @@ class Underpost {
42
44
  * @type {String}
43
45
  * @memberof Underpost
44
46
  */
45
- static version = 'v3.1.2';
47
+ static version = 'v3.2.0';
46
48
 
47
49
  /**
48
50
  * Required Node.js major version
@@ -126,6 +128,15 @@ class Underpost {
126
128
  static get db() {
127
129
  return UnderpostDB.API;
128
130
  }
131
+ /**
132
+ * Kubectl cli API
133
+ * @static
134
+ * @type {UnderpostKubectl.API}
135
+ * @memberof Underpost
136
+ */
137
+ static get kubectl() {
138
+ return UnderpostKubectl.API;
139
+ }
129
140
  /**
130
141
  * Deployment cli API
131
142
  * @static
@@ -280,6 +291,16 @@ class Underpost {
280
291
  static get tls() {
281
292
  return UnderpostTLS.API;
282
293
  }
294
+
295
+ /**
296
+ * Release orchestrator cli API
297
+ * @static
298
+ * @type {UnderpostRelease.API}
299
+ * @memberof Underpost
300
+ */
301
+ static get release() {
302
+ return UnderpostRelease.API;
303
+ }
283
304
  }
284
305
 
285
306
  if (!process.version || !process.version.startsWith(`${Underpost.majorNodejsVersion}.`))
@@ -300,6 +321,7 @@ export {
300
321
  UnderpostCluster,
301
322
  UnderpostDB,
302
323
  UnderpostDeploy,
324
+ UnderpostKubectl,
303
325
  UnderpostRootEnv,
304
326
  UnderpostFileStorage,
305
327
  UnderpostImage,
@@ -317,6 +339,7 @@ export {
317
339
  UnderpostBackup,
318
340
  UnderpostCron,
319
341
  UnderpostStartUp,
342
+ UnderpostRelease,
320
343
  UnderpostTLS,
321
344
  };
322
345
 
@@ -79,6 +79,7 @@ class ExpressService {
79
79
  peer,
80
80
  valkey,
81
81
  apiBaseHost,
82
+ grpc,
82
83
  redirectTarget,
83
84
  rootHostPath,
84
85
  confSSR,
@@ -135,7 +136,17 @@ class ExpressService {
135
136
  });
136
137
 
137
138
  // Static file serving
138
- app.use('/', express.static(directory ? directory : `.${rootHostPath}`));
139
+ app.use(
140
+ '/',
141
+ express.static(directory ? directory : `.${rootHostPath}`, {
142
+ setHeaders: (res, filePath) => {
143
+ if (filePath.includes('/assets/')) {
144
+ res.set('Access-Control-Allow-Origin', '*');
145
+ res.set('Cross-Origin-Resource-Policy', 'cross-origin');
146
+ }
147
+ },
148
+ }),
149
+ );
139
150
 
140
151
  // Handle redirection-only instances
141
152
  if (redirect) {
@@ -165,14 +176,7 @@ class ExpressService {
165
176
  });
166
177
  }
167
178
 
168
- // Swagger UI setup
169
- if (fs.existsSync(swaggerJsonPath)) {
170
- const swaggerDoc = JSON.parse(fs.readFileSync(swaggerJsonPath, 'utf8'));
171
- const swaggerUiOptions = await buildSwaggerUiOptions();
172
- app.use(swaggerPath, swaggerUi.serve, swaggerUi.setup(swaggerDoc, swaggerUiOptions));
173
- }
174
-
175
- // Security and CORS
179
+ // Security and CORS — must run before swagger-ui so CSP headers are included in swagger responses
176
180
  if (process.env.NODE_ENV === 'development' && useLocalSsl)
177
181
  origins = origins.map((origin) => origin.replace('http', 'https'));
178
182
 
@@ -180,6 +184,13 @@ class ExpressService {
180
184
  origin: origins,
181
185
  });
182
186
 
187
+ // Swagger UI setup (after security middleware so responses carry correct CSP headers)
188
+ if (fs.existsSync(swaggerJsonPath)) {
189
+ const swaggerDoc = JSON.parse(fs.readFileSync(swaggerJsonPath, 'utf8'));
190
+ const swaggerUiOptions = await buildSwaggerUiOptions();
191
+ app.use(swaggerPath, swaggerUi.serve, swaggerUi.setup(swaggerDoc, swaggerUiOptions));
192
+ }
193
+
183
194
  // Database and Valkey connections
184
195
  if (db && apis) await DataBaseProvider.load({ apis, host, path, db });
185
196
 
@@ -198,6 +209,12 @@ class ExpressService {
198
209
  });
199
210
  }
200
211
 
212
+ // gRPC server
213
+ if (path === '/' && grpc && grpc.module) {
214
+ const { GrpcServer } = await import(`../../grpc/${grpc.module}/grpc-server.js`);
215
+ await GrpcServer.start({ host, path, port: grpc.port || 50051 });
216
+ }
217
+
201
218
  // API router loading
202
219
  if (apis && apis.length > 0) {
203
220
  const authMiddleware = authMiddlewareFactory({ host, path });
@@ -20,8 +20,8 @@ RUN dnf -y update && \
20
20
  perl && \
21
21
  dnf clean all
22
22
 
23
- # --- Download and install XAMPP
24
- RUN curl -L -o /tmp/xampp-linux-installer.run "https://sourceforge.net/projects/xampp/files/XAMPP%20Linux/7.4.33/xampp-linux-x64-7.4.33-0-installer.run" && \
23
+ # --- Download and install XAMPP (PHP 8.2)
24
+ RUN curl -L -o /tmp/xampp-linux-installer.run "https://sourceforge.net/projects/xampp/files/XAMPP%20Linux/8.2.12/xampp-linux-x64-8.2.12-0-installer.run" && \
25
25
  chmod +x /tmp/xampp-linux-installer.run && \
26
26
  bash -c "/tmp/xampp-linux-installer.run --mode unattended" && \
27
27
  ln -sf /opt/lampp/lampp /usr/bin/lampp
@@ -31,6 +31,13 @@ RUN mkdir -p /opt/lampp/htdocs && \
31
31
  chown -R root:root /opt/lampp/htdocs && \
32
32
  chmod -R a+rX /opt/lampp/htdocs
33
33
 
34
+ # Add XAMPP binaries and /usr/local/bin to PATH for all shells
35
+ ENV PATH="/opt/lampp/bin:/usr/local/bin:${PATH}"
36
+ RUN echo 'export PATH="/opt/lampp/bin:/usr/local/bin:${PATH}"' > /etc/profile.d/lampp.sh
37
+
38
+ # Provide a no-op sendmail so PHP plugins don't error on mail calls
39
+ RUN printf '#!/bin/sh\ncat > /dev/null\n' > /usr/sbin/sendmail && chmod +x /usr/sbin/sendmail
40
+
34
41
  # Install Node.js
35
42
  RUN curl -fsSL https://rpm.nodesource.com/setup_24.x | bash -
36
43
  RUN dnf install nodejs -y
@@ -169,9 +169,9 @@ class LamppService {
169
169
  shellCd(`./engine-private/setup`);
170
170
 
171
171
  if (!process.argv.includes(`server`)) {
172
- // Download and run the XAMPP installer
172
+ // Download and run the XAMPP installer (PHP 8.2)
173
173
  shellExec(
174
- `curl -Lo xampp-linux-installer.run https://sourceforge.net/projects/xampp/files/XAMPP%20Linux/7.4.30/xampp-linux-x64-7.4.30-1-installer.run?from_af=true`,
174
+ `curl -Lo xampp-linux-installer.run https://sourceforge.net/projects/xampp/files/XAMPP%20Linux/8.2.12/xampp-linux-x64-8.2.12-0-installer.run`,
175
175
  );
176
176
  shellExec(`sudo chmod +x xampp-linux-installer.run`);
177
177
  shellExec(
@@ -237,7 +237,8 @@ Listen ${port}
237
237
 
238
238
  <VirtualHost *:${port}>
239
239
  DocumentRoot "${documentRoot}"
240
- ServerName ${host}:${port}
240
+ ServerName ${host}
241
+ UseCanonicalName Off
241
242
 
242
243
  <Directory "${documentRoot}">
243
244
  Options Indexes FollowSymLinks MultiViews
@@ -0,0 +1,64 @@
1
+ FROM rockylinux:9
2
+
3
+ # Update and install required packages
4
+ RUN dnf -y update && \
5
+ dnf -y install epel-release && \
6
+ dnf -y install --allowerasing \
7
+ bzip2 \
8
+ sudo \
9
+ curl \
10
+ unzip \
11
+ net-tools \
12
+ openssh-server \
13
+ nano \
14
+ vim-enhanced \
15
+ less \
16
+ openssl-devel \
17
+ wget \
18
+ git \
19
+ gnupg2 \
20
+ libnsl \
21
+ perl && \
22
+ dnf clean all
23
+
24
+ # --- Download and install XAMPP (PHP 8.2)
25
+ RUN curl -L -o /tmp/xampp-linux-installer.run "https://sourceforge.net/projects/xampp/files/XAMPP%20Linux/8.2.12/xampp-linux-x64-8.2.12-0-installer.run" && \
26
+ chmod +x /tmp/xampp-linux-installer.run && \
27
+ bash -c "/tmp/xampp-linux-installer.run --mode unattended" && \
28
+ ln -sf /opt/lampp/lampp /usr/bin/lampp
29
+
30
+ # --- Create /xampp/htdocs (static root) and set permissions
31
+ RUN mkdir -p /opt/lampp/htdocs && \
32
+ chown -R root:root /opt/lampp/htdocs && \
33
+ chmod -R a+rX /opt/lampp/htdocs
34
+
35
+ # Add XAMPP binaries and /usr/local/bin to PATH for all shells
36
+ ENV PATH="/opt/lampp/bin:/usr/local/bin:${PATH}"
37
+ RUN echo 'export PATH="/opt/lampp/bin:/usr/local/bin:${PATH}"' > /etc/profile.d/lampp.sh
38
+
39
+ # Provide a no-op sendmail so WP plugins don't error on mail calls
40
+ RUN printf '#!/bin/sh\ncat > /dev/null\n' > /usr/sbin/sendmail && chmod +x /usr/sbin/sendmail
41
+
42
+ # Install WP-CLI
43
+ RUN curl -sL https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar -o /usr/local/bin/wp && \
44
+ chmod +x /usr/local/bin/wp && \
45
+ wp --info --allow-root
46
+
47
+ # Install Node.js
48
+ RUN curl -fsSL https://rpm.nodesource.com/setup_24.x | bash -
49
+ RUN dnf install nodejs -y
50
+ RUN dnf clean all
51
+
52
+ # Verify Node.js and npm versions
53
+ RUN node --version
54
+ RUN npm --version
55
+
56
+ # Set working directory
57
+ WORKDIR /home/dd
58
+
59
+ EXPOSE 22
60
+ EXPOSE 80
61
+ EXPOSE 443
62
+ EXPOSE 3000-3100
63
+ EXPOSE 4000-4100
64
+