underpost 3.2.4 → 3.2.8
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/.github/workflows/release.cd.yml +1 -2
- package/CHANGELOG.md +268 -1
- package/CLI-HELP.md +26 -13
- package/Dockerfile +0 -4
- package/README.md +3 -3
- package/bin/build.js +13 -3
- package/bin/deploy.js +570 -1
- package/bin/file.js +5 -0
- package/conf.js +11 -2
- package/jsconfig.json +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -3
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -3
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -6
- package/manifests/deployment/dd-test-development/deployment.yaml +136 -66
- package/manifests/deployment/dd-test-development/proxy.yaml +41 -5
- package/package.json +20 -11
- package/src/api/core/core.controller.js +10 -10
- package/src/api/core/core.service.js +10 -10
- package/src/api/default/default.controller.js +10 -10
- package/src/api/default/default.service.js +10 -10
- package/src/api/document/document.controller.js +12 -12
- package/src/api/document/document.model.js +10 -16
- package/src/api/file/file.controller.js +8 -8
- package/src/api/file/file.model.js +10 -10
- package/src/api/file/file.service.js +36 -36
- package/src/api/test/test.controller.js +8 -8
- package/src/api/test/test.service.js +8 -8
- package/src/api/user/guest.service.js +99 -0
- package/src/api/user/user.controller.js +6 -6
- package/src/api/user/user.model.js +8 -13
- package/src/api/user/user.service.js +3 -20
- package/src/cli/deploy.js +33 -30
- package/src/cli/fs.js +62 -5
- package/src/cli/image.js +43 -1
- package/src/cli/index.js +5 -1
- package/src/cli/release.js +58 -2
- package/src/cli/repository.js +35 -3
- package/src/cli/run.js +304 -38
- package/src/cli/ssh.js +1 -1
- package/src/cli/static.js +43 -115
- package/src/client/Default.index.js +21 -33
- package/src/client/components/core/404.js +4 -4
- package/src/client/components/core/500.js +4 -4
- package/src/client/components/core/Account.js +73 -60
- package/src/client/components/core/AgGrid.js +23 -33
- package/src/client/components/core/Alert.js +12 -13
- package/src/client/components/core/AppStore.js +1 -1
- package/src/client/components/core/Auth.js +20 -32
- package/src/client/components/core/Badge.js +7 -13
- package/src/client/components/core/BtnIcon.js +15 -17
- package/src/client/components/core/CalendarCore.js +42 -63
- package/src/client/components/core/Chat.js +13 -15
- package/src/client/components/core/ClientEvents.js +87 -0
- package/src/client/components/core/ColorPaletteElement.js +309 -0
- package/src/client/components/core/Content.js +17 -14
- package/src/client/components/core/Css.js +15 -71
- package/src/client/components/core/CssCore.js +12 -16
- package/src/client/components/core/D3Chart.js +4 -4
- package/src/client/components/core/Docs.js +60 -59
- package/src/client/components/core/DropDown.js +69 -91
- package/src/client/components/core/EventBus.js +92 -0
- package/src/client/components/core/EventsUI.js +14 -17
- package/src/client/components/core/FileExplorer.js +102 -234
- package/src/client/components/core/FullScreen.js +47 -75
- package/src/client/components/core/Input.js +24 -69
- package/src/client/components/core/Keyboard.js +25 -18
- package/src/client/components/core/KeyboardAvoidance.js +145 -0
- package/src/client/components/core/LoadingAnimation.js +25 -31
- package/src/client/components/core/LogIn.js +41 -41
- package/src/client/components/core/LogOut.js +23 -14
- package/src/client/components/core/Modal.js +397 -176
- package/src/client/components/core/NotificationManager.js +14 -18
- package/src/client/components/core/Panel.js +54 -50
- package/src/client/components/core/PanelForm.js +25 -125
- package/src/client/components/core/Polyhedron.js +110 -214
- package/src/client/components/core/PublicProfile.js +39 -32
- package/src/client/components/core/Recover.js +52 -48
- package/src/client/components/core/Responsive.js +88 -32
- package/src/client/components/core/RichText.js +9 -18
- package/src/client/components/core/Router.js +24 -3
- package/src/client/components/core/SearchBox.js +37 -37
- package/src/client/components/core/SignUp.js +39 -30
- package/src/client/components/core/SocketIo.js +31 -2
- package/src/client/components/core/SocketIoHandler.js +6 -6
- package/src/client/components/core/ToggleSwitch.js +8 -20
- package/src/client/components/core/ToolTip.js +5 -17
- package/src/client/components/core/Translate.js +56 -59
- package/src/client/components/core/Validator.js +26 -16
- package/src/client/components/core/Wallet.js +15 -26
- package/src/client/components/core/Worker.js +140 -25
- package/src/client/components/core/windowGetDimensions.js +7 -7
- package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +87 -87
- package/src/client/components/default/CssDefault.js +12 -12
- package/src/client/components/default/LogInDefault.js +6 -4
- package/src/client/components/default/LogOutDefault.js +6 -4
- package/src/client/components/default/RouterDefault.js +47 -0
- package/src/client/components/default/SettingsDefault.js +4 -4
- package/src/client/components/default/SignUpDefault.js +6 -4
- package/src/client/components/default/TranslateDefault.js +3 -3
- package/src/client/services/core/core.service.js +17 -49
- package/src/client/services/default/default.management.js +139 -242
- package/src/client/services/default/default.service.js +10 -16
- package/src/client/services/document/document.service.js +14 -19
- package/src/client/services/file/file.service.js +8 -13
- package/src/client/services/test/test.service.js +8 -13
- package/src/client/services/user/guest.service.js +79 -0
- package/src/client/services/user/user.management.js +5 -5
- package/src/client/services/user/user.service.js +14 -20
- package/src/client/ssr/body/404.js +3 -3
- package/src/client/ssr/body/500.js +3 -3
- package/src/client/ssr/body/CacheControl.js +5 -2
- package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
- package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
- package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
- package/src/client/ssr/offline/Maintenance.js +12 -11
- package/src/client/ssr/offline/NoNetworkConnection.js +3 -3
- package/src/client/ssr/pages/Test.js +2 -2
- package/src/client/sw/core.sw.js +212 -0
- package/src/index.js +1 -1
- package/src/runtime/express/Dockerfile +4 -4
- package/src/runtime/lampp/Dockerfile +8 -7
- package/src/runtime/wp/Dockerfile +11 -17
- package/src/server/backup.js +1 -2
- package/src/server/client-build-docs.js +45 -46
- package/src/server/client-build.js +334 -60
- package/src/server/client-formatted.js +47 -16
- package/src/server/conf.js +29 -13
- package/src/server/cron.js +6 -8
- package/src/server/dns.js +2 -1
- package/src/server/ipfs-client.js +232 -91
- package/src/server/process.js +13 -27
- package/src/server/start.js +6 -3
- package/src/server/valkey.js +134 -235
- package/tsconfig.docs.json +15 -0
- package/typedoc.json +20 -0
- package/jsdoc.json +0 -52
- package/src/client/components/core/ColorPalette.js +0 -5267
- package/src/client/components/core/JoyStick.js +0 -80
- package/src/client/components/default/RoutesDefault.js +0 -49
- package/src/client/sw/default.sw.js +0 -127
- package/src/client/sw/template.sw.js +0 -84
|
@@ -19,7 +19,7 @@ const main = () => {
|
|
|
19
19
|
es: 'Volver a <br> la pagina principal',
|
|
20
20
|
},
|
|
21
21
|
},
|
|
22
|
-
|
|
22
|
+
instance: function (id) {
|
|
23
23
|
return this.Data[id][getLang()] ? this.Data[id][getLang()] : this.Data[id]['en'];
|
|
24
24
|
},
|
|
25
25
|
};
|
|
@@ -49,9 +49,9 @@ const main = () => {
|
|
|
49
49
|
${icon}
|
|
50
50
|
<br />
|
|
51
51
|
<br />
|
|
52
|
-
<br />${Translate.
|
|
52
|
+
<br />${Translate.instance('no-internet-connection')} <br />
|
|
53
53
|
<br />
|
|
54
|
-
<a target="_top" href="${location.origin}">${Translate.
|
|
54
|
+
<a target="_top" href="${location.origin}">${Translate.instance('back')}</a>
|
|
55
55
|
</div>`,
|
|
56
56
|
);
|
|
57
57
|
};
|
|
@@ -15,7 +15,7 @@ const main = () => {
|
|
|
15
15
|
es: 'Volver a <br> la pagina principal',
|
|
16
16
|
},
|
|
17
17
|
},
|
|
18
|
-
|
|
18
|
+
instance: function (id) {
|
|
19
19
|
return this.Data[id][getLang()] ? this.Data[id][getLang()] : this.Data[id]['en'];
|
|
20
20
|
},
|
|
21
21
|
};
|
|
@@ -182,7 +182,7 @@ const main = () => {
|
|
|
182
182
|
<span class="bold">Test Page</span>
|
|
183
183
|
<br />
|
|
184
184
|
<br />
|
|
185
|
-
<a target="_top" href="${location.origin}">${Translate.
|
|
185
|
+
<a target="_top" href="${location.origin}">${Translate.instance('back')}</a>
|
|
186
186
|
</div>`,
|
|
187
187
|
);
|
|
188
188
|
};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// Workbox modules are bundled inline by esbuild — no CDN, no importScripts.
|
|
2
|
+
import { setCacheNameDetails, clientsClaim } from 'workbox-core';
|
|
3
|
+
import { precacheAndRoute, cleanupOutdatedCaches, matchPrecache } from 'workbox-precaching';
|
|
4
|
+
import { registerRoute, setCatchHandler } from 'workbox-routing';
|
|
5
|
+
import { StaleWhileRevalidate, NetworkFirst, NetworkOnly } from 'workbox-strategies';
|
|
6
|
+
import { CacheableResponsePlugin } from 'workbox-cacheable-response';
|
|
7
|
+
import { ExpirationPlugin } from 'workbox-expiration';
|
|
8
|
+
import { BackgroundSyncPlugin } from 'workbox-background-sync';
|
|
9
|
+
|
|
10
|
+
// ─── Runtime config injected by client-build.js ───────────────────────────────
|
|
11
|
+
const CACHE_PREFIX = self.renderPayload?.CACHE_PREFIX || 'engine-core-v3';
|
|
12
|
+
const PRE_CACHED_RESOURCES = Array.isArray(self.renderPayload?.PRE_CACHED_RESOURCES)
|
|
13
|
+
? self.renderPayload.PRE_CACHED_RESOURCES
|
|
14
|
+
: [];
|
|
15
|
+
const PROXY_PATH = self.renderPayload?.PROXY_PATH || '/';
|
|
16
|
+
const OFFLINE_PATH = self.renderPayload?.OFFLINE_PATH || '/offline';
|
|
17
|
+
const MAINTENANCE_PATH = self.renderPayload?.MAINTENANCE_PATH || '/maintenance';
|
|
18
|
+
const proxyBase = PROXY_PATH === '/' ? '' : PROXY_PATH;
|
|
19
|
+
const normalizeRoutePath = (candidatePath, fallbackPath) => {
|
|
20
|
+
const routePath = typeof candidatePath === 'string' && candidatePath.length > 0 ? candidatePath : fallbackPath;
|
|
21
|
+
const withLeadingSlash = routePath.startsWith('/') ? routePath : `/${routePath}`;
|
|
22
|
+
const withoutTrailingSlash = withLeadingSlash.replace(/\/+$/, '');
|
|
23
|
+
return withoutTrailingSlash.length > 0 ? withoutTrailingSlash : '/';
|
|
24
|
+
};
|
|
25
|
+
const offlinePath = normalizeRoutePath(OFFLINE_PATH, '/offline');
|
|
26
|
+
const maintenancePath = normalizeRoutePath(MAINTENANCE_PATH, '/maintenance');
|
|
27
|
+
const toRouteIndexUrl = (routePath) => `${proxyBase}${routePath === '/' ? '' : routePath}/index.html`;
|
|
28
|
+
const offlineUrl = toRouteIndexUrl(offlinePath);
|
|
29
|
+
const maintenanceUrl = toRouteIndexUrl(maintenancePath);
|
|
30
|
+
|
|
31
|
+
// Dedicated cache for fallback pages — populated independently from precacheAndRoute
|
|
32
|
+
// so offline/maintenance pages are always available even if the main precache install fails.
|
|
33
|
+
const FALLBACK_CACHE_NAME = `${CACHE_PREFIX}-fallbacks`;
|
|
34
|
+
|
|
35
|
+
const getFallbackResponse = async (preferMaintenance) => {
|
|
36
|
+
const cache = await caches.open(FALLBACK_CACHE_NAME);
|
|
37
|
+
if (preferMaintenance) {
|
|
38
|
+
return (
|
|
39
|
+
(await cache.match(maintenanceUrl)) ||
|
|
40
|
+
(await matchPrecache(maintenanceUrl)) ||
|
|
41
|
+
(await cache.match(offlineUrl)) ||
|
|
42
|
+
(await matchPrecache(offlineUrl)) ||
|
|
43
|
+
Response.error()
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return (
|
|
47
|
+
(await cache.match(offlineUrl)) ||
|
|
48
|
+
(await matchPrecache(offlineUrl)) ||
|
|
49
|
+
(await cache.match(maintenanceUrl)) ||
|
|
50
|
+
(await matchPrecache(maintenanceUrl)) ||
|
|
51
|
+
Response.error()
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// ─── Core setup ───────────────────────────────────────────────────────────────
|
|
56
|
+
setCacheNameDetails({ prefix: CACHE_PREFIX });
|
|
57
|
+
clientsClaim();
|
|
58
|
+
|
|
59
|
+
self.addEventListener('install', (event) => {
|
|
60
|
+
self.skipWaiting();
|
|
61
|
+
// Cache fallback pages in a dedicated cache so they are available even if
|
|
62
|
+
// precacheAndRoute fails (e.g. some asset in the manifest returns non-200).
|
|
63
|
+
event.waitUntil(
|
|
64
|
+
caches.open(FALLBACK_CACHE_NAME).then((cache) =>
|
|
65
|
+
// Try together first; fall back to individual adds so a single failure
|
|
66
|
+
// does not prevent the other page from being cached.
|
|
67
|
+
cache
|
|
68
|
+
.addAll([offlineUrl, maintenanceUrl])
|
|
69
|
+
.catch(() => Promise.all([cache.add(offlineUrl).catch(() => {}), cache.add(maintenanceUrl).catch(() => {})])),
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
self.addEventListener('activate', (event) => {
|
|
75
|
+
event.waitUntil(
|
|
76
|
+
(async () => {
|
|
77
|
+
if ('navigationPreload' in self.registration) {
|
|
78
|
+
await self.registration.navigationPreload.enable();
|
|
79
|
+
}
|
|
80
|
+
})(),
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
self.addEventListener('message', (event) => {
|
|
85
|
+
const payload = event.data || {};
|
|
86
|
+
|
|
87
|
+
if (payload.status === 'skipWaiting') {
|
|
88
|
+
self.skipWaiting();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (payload.status === 'workbox-reset') {
|
|
93
|
+
event.waitUntil(
|
|
94
|
+
(async () => {
|
|
95
|
+
const cacheNames = await caches.keys();
|
|
96
|
+
await Promise.all(cacheNames.map((cacheName) => caches.delete(cacheName)));
|
|
97
|
+
if (event.ports && event.ports[0]) {
|
|
98
|
+
event.ports[0].postMessage({ status: 'workbox-reset-done', deleted: cacheNames.length });
|
|
99
|
+
}
|
|
100
|
+
})(),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// ─── Precaching ───────────────────────────────────────────────────────────────
|
|
106
|
+
precacheAndRoute(PRE_CACHED_RESOURCES.map((url) => ({ url, revision: null })));
|
|
107
|
+
cleanupOutdatedCaches();
|
|
108
|
+
|
|
109
|
+
// ─── Static assets: StaleWhileRevalidate ─────────────────────────────────────
|
|
110
|
+
registerRoute(
|
|
111
|
+
({ request, url }) =>
|
|
112
|
+
request.method === 'GET' &&
|
|
113
|
+
url.origin === self.location.origin &&
|
|
114
|
+
['style', 'script', 'font', 'image'].includes(request.destination),
|
|
115
|
+
new StaleWhileRevalidate({
|
|
116
|
+
cacheName: `${CACHE_PREFIX}-assets`,
|
|
117
|
+
plugins: [
|
|
118
|
+
new CacheableResponsePlugin({
|
|
119
|
+
statuses: [0, 200],
|
|
120
|
+
}),
|
|
121
|
+
new ExpirationPlugin({
|
|
122
|
+
maxEntries: 350,
|
|
123
|
+
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
|
|
124
|
+
}),
|
|
125
|
+
],
|
|
126
|
+
}),
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
// ─── API GET: NetworkFirst with short timeout ─────────────────────────────────
|
|
130
|
+
registerRoute(
|
|
131
|
+
({ request, url }) => request.method === 'GET' && url.pathname.includes('/api/'),
|
|
132
|
+
new NetworkFirst({
|
|
133
|
+
cacheName: `${CACHE_PREFIX}-api-get`,
|
|
134
|
+
networkTimeoutSeconds: 5,
|
|
135
|
+
plugins: [
|
|
136
|
+
new CacheableResponsePlugin({
|
|
137
|
+
statuses: [0, 200],
|
|
138
|
+
}),
|
|
139
|
+
new ExpirationPlugin({
|
|
140
|
+
maxEntries: 120,
|
|
141
|
+
maxAgeSeconds: 5 * 60, // 5 minutes
|
|
142
|
+
}),
|
|
143
|
+
],
|
|
144
|
+
}),
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// ─── API mutations: NetworkOnly + background sync replay ─────────────────────
|
|
148
|
+
registerRoute(
|
|
149
|
+
({ request, url }) => request.method !== 'GET' && url.pathname.includes('/api/'),
|
|
150
|
+
new NetworkOnly({
|
|
151
|
+
plugins: [
|
|
152
|
+
new BackgroundSyncPlugin('api-mutation-queue', {
|
|
153
|
+
maxRetentionTime: 24 * 60, // 24 hours
|
|
154
|
+
}),
|
|
155
|
+
],
|
|
156
|
+
}),
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// ─── Navigation: NetworkFirst with offline fallback ───────────────────────────
|
|
160
|
+
registerRoute(
|
|
161
|
+
({ request }) => request.mode === 'navigate',
|
|
162
|
+
async ({ event }) => {
|
|
163
|
+
const navigationStrategy = new NetworkFirst({
|
|
164
|
+
cacheName: `${CACHE_PREFIX}-pages`,
|
|
165
|
+
networkTimeoutSeconds: 4,
|
|
166
|
+
plugins: [
|
|
167
|
+
new CacheableResponsePlugin({
|
|
168
|
+
statuses: [0, 200],
|
|
169
|
+
}),
|
|
170
|
+
new ExpirationPlugin({
|
|
171
|
+
maxEntries: 60,
|
|
172
|
+
maxAgeSeconds: 12 * 60 * 60, // 12 hours
|
|
173
|
+
}),
|
|
174
|
+
],
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Distinguish server-down (online but unreachable) from no-network (offline).
|
|
178
|
+
// navigator.onLine is false only when the device has no network at all.
|
|
179
|
+
const isOnline = () => typeof navigator !== 'undefined' && navigator.onLine !== false;
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const preload = await event.preloadResponse;
|
|
183
|
+
if (preload) {
|
|
184
|
+
if (preload.status >= 500) {
|
|
185
|
+
return getFallbackResponse(true);
|
|
186
|
+
}
|
|
187
|
+
return preload;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const networkResponse = await navigationStrategy.handle({ event, request: event.request });
|
|
191
|
+
if (networkResponse && networkResponse.status >= 500) {
|
|
192
|
+
return getFallbackResponse(true);
|
|
193
|
+
}
|
|
194
|
+
return networkResponse;
|
|
195
|
+
} catch (_) {
|
|
196
|
+
// If device reports it has network but the request failed, it means the
|
|
197
|
+
// server is unreachable/down → show maintenance. True offline → show offline.
|
|
198
|
+
return getFallbackResponse(isOnline());
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// ─── Global catch handler ─────────────────────────────────────────────────────
|
|
204
|
+
setCatchHandler(async ({ request }) => {
|
|
205
|
+
if (request.mode === 'navigate') {
|
|
206
|
+
return getFallbackResponse(typeof navigator !== 'undefined' && navigator.onLine !== false);
|
|
207
|
+
}
|
|
208
|
+
return new Response(JSON.stringify({ status: 'error', message: 'request failed' }), {
|
|
209
|
+
status: 503,
|
|
210
|
+
headers: { 'Content-Type': 'application/json' },
|
|
211
|
+
});
|
|
212
|
+
});
|
package/src/index.js
CHANGED
|
@@ -30,11 +30,11 @@ RUN dnf clean all
|
|
|
30
30
|
RUN node --version
|
|
31
31
|
RUN npm --version
|
|
32
32
|
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
RUN
|
|
33
|
+
# Install underpost ci/cd cli
|
|
34
|
+
RUN npm install -g underpost
|
|
35
|
+
RUN underpost --version
|
|
36
36
|
|
|
37
|
-
#
|
|
37
|
+
# Create working directory
|
|
38
38
|
WORKDIR /home/dd
|
|
39
39
|
|
|
40
40
|
# Expose necessary ports
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
FROM rockylinux:9
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# System packages
|
|
4
4
|
RUN dnf -y update && \
|
|
5
5
|
dnf -y install epel-release && \
|
|
6
6
|
dnf -y install --allowerasing \
|
|
@@ -20,13 +20,14 @@ RUN dnf -y update && \
|
|
|
20
20
|
perl && \
|
|
21
21
|
dnf clean all
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
# Download and install XAMPP (PHP 8.2)
|
|
24
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" && \
|
|
25
26
|
chmod +x /tmp/xampp-linux-installer.run && \
|
|
26
27
|
bash -c "/tmp/xampp-linux-installer.run --mode unattended" && \
|
|
27
28
|
ln -sf /opt/lampp/lampp /usr/bin/lampp
|
|
28
29
|
|
|
29
|
-
#
|
|
30
|
+
# Create /xampp/htdocs (static root) and set permissions
|
|
30
31
|
RUN mkdir -p /opt/lampp/htdocs && \
|
|
31
32
|
chown -R root:root /opt/lampp/htdocs && \
|
|
32
33
|
chmod -R a+rX /opt/lampp/htdocs
|
|
@@ -47,11 +48,11 @@ RUN dnf clean all
|
|
|
47
48
|
RUN node --version
|
|
48
49
|
RUN npm --version
|
|
49
50
|
|
|
50
|
-
#
|
|
51
|
-
|
|
52
|
-
RUN
|
|
51
|
+
# Install underpost CLI
|
|
52
|
+
RUN npm install -g underpost
|
|
53
|
+
RUN underpost --version
|
|
53
54
|
|
|
54
|
-
#
|
|
55
|
+
# Runtime root expected by startup/build scripts.
|
|
55
56
|
WORKDIR /home/dd
|
|
56
57
|
|
|
57
58
|
EXPOSE 22
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
FROM rockylinux:9
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# System packages
|
|
4
4
|
RUN dnf -y update && \
|
|
5
5
|
dnf -y install epel-release && \
|
|
6
6
|
dnf -y install --allowerasing \
|
|
7
7
|
bzip2 \
|
|
8
8
|
sudo \
|
|
9
9
|
curl \
|
|
10
|
-
unzip \
|
|
11
10
|
net-tools \
|
|
12
11
|
openssh-server \
|
|
13
12
|
nano \
|
|
@@ -21,13 +20,13 @@ RUN dnf -y update && \
|
|
|
21
20
|
perl && \
|
|
22
21
|
dnf clean all
|
|
23
22
|
|
|
24
|
-
#
|
|
23
|
+
# Download and install XAMPP (PHP 8.2)
|
|
25
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" && \
|
|
26
25
|
chmod +x /tmp/xampp-linux-installer.run && \
|
|
27
26
|
bash -c "/tmp/xampp-linux-installer.run --mode unattended" && \
|
|
28
27
|
ln -sf /opt/lampp/lampp /usr/bin/lampp
|
|
29
28
|
|
|
30
|
-
#
|
|
29
|
+
# Create /xampp/htdocs (static root) and set permissions
|
|
31
30
|
RUN mkdir -p /opt/lampp/htdocs && \
|
|
32
31
|
chown -R root:root /opt/lampp/htdocs && \
|
|
33
32
|
chmod -R a+rX /opt/lampp/htdocs
|
|
@@ -39,25 +38,20 @@ RUN echo 'export PATH="/opt/lampp/bin:/usr/local/bin:${PATH}"' > /etc/profile.d/
|
|
|
39
38
|
# Provide a no-op sendmail so WP plugins don't error on mail calls
|
|
40
39
|
RUN printf '#!/bin/sh\ncat > /dev/null\n' > /usr/sbin/sendmail && chmod +x /usr/sbin/sendmail
|
|
41
40
|
|
|
41
|
+
# Install Node.js (includes npm)
|
|
42
|
+
RUN curl -fsSL https://rpm.nodesource.com/setup_24.x | bash - && \
|
|
43
|
+
dnf install -y nodejs && \
|
|
44
|
+
dnf clean all
|
|
45
|
+
|
|
42
46
|
# Install WP-CLI
|
|
43
47
|
RUN curl -sL https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar -o /usr/local/bin/wp && \
|
|
44
48
|
chmod +x /usr/local/bin/wp && \
|
|
45
49
|
wp --info --allow-root
|
|
46
50
|
|
|
47
|
-
# Install
|
|
48
|
-
RUN
|
|
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
|
-
# Create non-root user for secure container execution (cron jobs, init containers)
|
|
57
|
-
# Deployment containers override to root via securityContext when npm install -g is needed
|
|
58
|
-
RUN useradd -m -u 1000 -s /bin/bash dd
|
|
51
|
+
# Install underpost CLI
|
|
52
|
+
RUN npm install -g underpost
|
|
59
53
|
|
|
60
|
-
#
|
|
54
|
+
# Runtime root expected by startup/build scripts.
|
|
61
55
|
WORKDIR /home/dd
|
|
62
56
|
|
|
63
57
|
EXPOSE 22
|
package/src/server/backup.js
CHANGED
|
@@ -50,8 +50,7 @@ class BackUp {
|
|
|
50
50
|
for (const _deployId of deployList.split(',')) {
|
|
51
51
|
const deployId = _deployId.trim();
|
|
52
52
|
if (!deployId) continue;
|
|
53
|
-
|
|
54
|
-
const dbCommand = `node bin db ${options.git ? '--git --force-clone ' : ''}--export --primary-pod${clusterFlag} ${deployId}`;
|
|
53
|
+
const dbCommand = `node bin db ${options.git ? '--git --force-clone ' : ''}--export --primary-pod --preserveUUID${clusterFlag} ${deployId}`;
|
|
55
54
|
const repoCommand = `node bin db --repo-backup${clusterFlag} ${deployId}`;
|
|
56
55
|
|
|
57
56
|
// Pass GITHUB_TOKEN and GITHUB_USERNAME ephemerally through the SSH command
|
|
@@ -324,59 +324,56 @@ const buildApiDocs = async ({
|
|
|
324
324
|
};
|
|
325
325
|
|
|
326
326
|
/**
|
|
327
|
-
* Builds JSDoc
|
|
327
|
+
* Builds API documentation using TypeDoc (generates a modern static site from JSDoc-annotated JS).
|
|
328
|
+
* Config is read from the base typedoc JSON, merged with runtime values, written to a temporary
|
|
329
|
+
* file, and deleted after the build — the base config file is never mutated on disk.
|
|
328
330
|
* @function buildJsDocs
|
|
329
331
|
* @memberof clientBuildDocs
|
|
330
|
-
* @param {Object} options -
|
|
332
|
+
* @param {Object} options - TypeDoc build options
|
|
331
333
|
* @param {string} options.host - The hostname for the documentation
|
|
332
334
|
* @param {string} options.path - The base path for the documentation
|
|
333
335
|
* @param {Object} options.metadata - Metadata for the documentation
|
|
334
|
-
* @param {string} options.publicClientId - Client ID used to resolve the
|
|
336
|
+
* @param {string} options.publicClientId - Client ID used to resolve the references directory
|
|
335
337
|
* @param {Object} options.docs - Documentation config from server conf
|
|
336
|
-
* @param {string} options.docs.jsJsonPath - Path to the
|
|
338
|
+
* @param {string} options.docs.jsJsonPath - Path to the base typedoc JSON config file
|
|
339
|
+
* @param {string} options.docsDestination - Resolved output path for the generated docs
|
|
337
340
|
*/
|
|
338
|
-
const buildJsDocs = async ({ host, path, metadata = {}, publicClientId, docs }) => {
|
|
341
|
+
const buildJsDocs = async ({ host, path, metadata = {}, publicClientId, docs, docsDestination }) => {
|
|
339
342
|
const logger = loggerFactory(import.meta);
|
|
340
343
|
|
|
341
|
-
const
|
|
342
|
-
if (!fs.existsSync(
|
|
343
|
-
logger.warn('
|
|
344
|
+
const typedocConfigPath = docs.jsJsonPath;
|
|
345
|
+
if (!fs.existsSync(typedocConfigPath)) {
|
|
346
|
+
logger.warn('typedoc config not found, skipping', typedocConfigPath);
|
|
344
347
|
return;
|
|
345
348
|
}
|
|
346
|
-
const
|
|
347
|
-
logger.info('using
|
|
349
|
+
const baseConfig = JSON.parse(fs.readFileSync(typedocConfigPath, 'utf8'));
|
|
350
|
+
logger.info('using typedoc config', typedocConfigPath);
|
|
348
351
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
352
|
+
// Build runtime config in memory — never mutate the base config file
|
|
353
|
+
// tsconfig must be absolute so TypeDoc resolves it regardless of where the
|
|
354
|
+
// tmp config file is located on disk.
|
|
355
|
+
const runtimeConfig = {
|
|
356
|
+
...baseConfig,
|
|
357
|
+
tsconfig: fs.realpathSync(baseConfig.tsconfig || './tsconfig.docs.json'),
|
|
358
|
+
out: docsDestination,
|
|
359
|
+
name: metadata?.title || baseConfig.name,
|
|
360
|
+
favicon: `./public/${host}${path === '/' ? '/' : `${path}/`}favicon.ico`,
|
|
361
|
+
};
|
|
354
362
|
|
|
363
|
+
// Include extra reference documents as TypeDoc document pages
|
|
364
|
+
// TypeDoc 0.28+: option is `projectDocuments`, not `documents`
|
|
355
365
|
if (Array.isArray(docs.references) && docs.references.length > 0) {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
if (fs.existsSync(refPath)) {
|
|
359
|
-
const fileName = refPath.split('/').pop();
|
|
360
|
-
fs.copySync(refPath, `${tutorialsPath}/${fileName}`);
|
|
361
|
-
logger.info('copied reference to tutorials', refPath);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
366
|
+
runtimeConfig.projectDocuments = docs.references.filter((p) => fs.existsSync(p));
|
|
367
|
+
if (runtimeConfig.projectDocuments.length > 0) logger.info('typedoc documents', runtimeConfig.projectDocuments);
|
|
364
368
|
}
|
|
365
369
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
jsDocsConfig.opts.theme_opts.sections.push('Tutorials');
|
|
370
|
-
}
|
|
371
|
-
logger.info('build jsdoc tutorials', tutorialsPath);
|
|
372
|
-
} else {
|
|
373
|
-
delete jsDocsConfig.opts.tutorials;
|
|
374
|
-
}
|
|
370
|
+
const tmpConfigPath = `.typedoc.tmp.json`;
|
|
371
|
+
fs.writeFileSync(tmpConfigPath, JSON.stringify(runtimeConfig, null, 2), 'utf8');
|
|
372
|
+
logger.warn('build typedoc view', docsDestination);
|
|
375
373
|
|
|
376
|
-
|
|
377
|
-
logger.warn('build jsdoc view', jsDocsConfig.opts.destination);
|
|
374
|
+
shellExec(`node_modules/.bin/typedoc --options ${tmpConfigPath}`, { silent: true });
|
|
378
375
|
|
|
379
|
-
|
|
376
|
+
fs.removeSync(tmpConfigPath);
|
|
380
377
|
};
|
|
381
378
|
|
|
382
379
|
/**
|
|
@@ -384,17 +381,13 @@ const buildJsDocs = async ({ host, path, metadata = {}, publicClientId, docs })
|
|
|
384
381
|
* @function buildCoverage
|
|
385
382
|
* @memberof clientBuildDocs
|
|
386
383
|
* @param {Object} options - Coverage build options
|
|
387
|
-
* @param {string} options.host - The hostname for the coverage
|
|
388
|
-
* @param {string} options.path - The base path for the coverage
|
|
389
384
|
* @param {Object} options.docs - Documentation config from server conf
|
|
390
|
-
* @param {string} options.docs.coveragePath - Directory where
|
|
385
|
+
* @param {string} options.docs.coveragePath - Directory where coverage reports are generated
|
|
386
|
+
* @param {string} options.docsDestination - Resolved output path where docs were built
|
|
391
387
|
*/
|
|
392
|
-
const buildCoverage = async ({
|
|
388
|
+
const buildCoverage = async ({ docs, docsDestination }) => {
|
|
393
389
|
const logger = loggerFactory(import.meta);
|
|
394
|
-
const
|
|
395
|
-
const jsDocsConfig = JSON.parse(fs.readFileSync(jsDocSourcePath, 'utf8'));
|
|
396
|
-
const coveragePath = docs.coveragePath;
|
|
397
|
-
const coverageOutputDir = docs.coverageOutputDir || 'coverage';
|
|
390
|
+
const { coveragePath, coverageOutputDir = 'coverage' } = docs;
|
|
398
391
|
|
|
399
392
|
const coverageOutputPath = `${coveragePath}/coverage`;
|
|
400
393
|
if (!fs.existsSync(coverageOutputPath)) {
|
|
@@ -412,7 +405,7 @@ const buildCoverage = async ({ host, path, docs }) => {
|
|
|
412
405
|
}
|
|
413
406
|
|
|
414
407
|
if (fs.existsSync(coverageOutputPath) && fs.readdirSync(coverageOutputPath).length > 0) {
|
|
415
|
-
const coverageBuildPath = `${
|
|
408
|
+
const coverageBuildPath = `${docsDestination}${coverageOutputDir}`;
|
|
416
409
|
fs.mkdirSync(coverageBuildPath, { recursive: true });
|
|
417
410
|
// Hardhat 3 outputs HTML to coverage/html/; Hardhat 2 / c8 output directly to coverage/
|
|
418
411
|
const coverageHtmlSubdir = `${coverageOutputPath}/html`;
|
|
@@ -453,8 +446,14 @@ const buildDocs = async ({
|
|
|
453
446
|
packageData,
|
|
454
447
|
docs,
|
|
455
448
|
}) => {
|
|
456
|
-
|
|
457
|
-
|
|
449
|
+
const pathPrefix = path === '/' ? '/' : `${path}/`;
|
|
450
|
+
// TypeDoc output is versioned: served at /docs/engine/{version}/
|
|
451
|
+
const version = (packageData?.version || '').replace(/^v/, '');
|
|
452
|
+
const jsDocsDestination = `./public/${host}${pathPrefix}docs/engine/${version}/`;
|
|
453
|
+
// Coverage output at /docs/coverage/ (or /docs/{coverageOutputDir}/)
|
|
454
|
+
const coverageBaseDestination = `./public/${host}${pathPrefix}docs/`;
|
|
455
|
+
await buildJsDocs({ host, path, metadata, publicClientId, docs, docsDestination: jsDocsDestination });
|
|
456
|
+
await buildCoverage({ docs, docsDestination: coverageBaseDestination });
|
|
458
457
|
await buildApiDocs({
|
|
459
458
|
host,
|
|
460
459
|
path,
|