underpost 3.2.5 → 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 +251 -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 +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- 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 +57 -1
- package/src/cli/repository.js +35 -3
- package/src/cli/run.js +300 -35
- 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/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 +5 -4
- 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
|
@@ -25,11 +25,11 @@ const handleRequest = (serviceMethod) => async (req, res, options) => {
|
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
post
|
|
30
|
-
get
|
|
31
|
-
delete
|
|
32
|
-
put
|
|
33
|
-
}
|
|
28
|
+
class UserController {
|
|
29
|
+
static post = handleRequest(UserService.post);
|
|
30
|
+
static get = handleRequest(UserService.get);
|
|
31
|
+
static delete = handleRequest(UserService.delete);
|
|
32
|
+
static put = handleRequest(UserService.put);
|
|
33
|
+
}
|
|
34
34
|
|
|
35
35
|
export { UserController };
|
|
@@ -3,7 +3,6 @@ import validator from 'validator';
|
|
|
3
3
|
import { userRoleEnum } from '../../client/components/core/CommonJs.js';
|
|
4
4
|
import crypto from 'crypto';
|
|
5
5
|
// https://mongoosejs.com/docs/2.7.x/docs/schematypes.html
|
|
6
|
-
|
|
7
6
|
const UserSchema = new Schema(
|
|
8
7
|
{
|
|
9
8
|
email: {
|
|
@@ -88,13 +87,10 @@ const UserSchema = new Schema(
|
|
|
88
87
|
timestamps: true,
|
|
89
88
|
},
|
|
90
89
|
);
|
|
91
|
-
|
|
92
90
|
const UserModel = model('User', UserSchema);
|
|
93
|
-
|
|
94
91
|
const ProviderSchema = UserSchema;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
select: {
|
|
92
|
+
class UserDto {
|
|
93
|
+
static select = {
|
|
98
94
|
get: () => {
|
|
99
95
|
return {
|
|
100
96
|
_id: 1,
|
|
@@ -112,8 +108,8 @@ const UserDto = {
|
|
|
112
108
|
getAll: () => {
|
|
113
109
|
return { _id: 1 };
|
|
114
110
|
},
|
|
115
|
-
}
|
|
116
|
-
public
|
|
111
|
+
};
|
|
112
|
+
static public = {
|
|
117
113
|
get: () => {
|
|
118
114
|
return {
|
|
119
115
|
_id: 1,
|
|
@@ -125,8 +121,8 @@ const UserDto = {
|
|
|
125
121
|
updatedAt: 1,
|
|
126
122
|
};
|
|
127
123
|
},
|
|
128
|
-
}
|
|
129
|
-
auth
|
|
124
|
+
};
|
|
125
|
+
static auth = {
|
|
130
126
|
payload: (user, jwtid, ip, userAgent, host, path) => {
|
|
131
127
|
const tokenPayload = {
|
|
132
128
|
_id: user._id.toString(),
|
|
@@ -141,7 +137,6 @@ const UserDto = {
|
|
|
141
137
|
};
|
|
142
138
|
return tokenPayload;
|
|
143
139
|
},
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
140
|
+
};
|
|
141
|
+
}
|
|
147
142
|
export { UserSchema, UserModel, userRoleEnum, ProviderSchema, UserDto };
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
refreshSessionAndToken,
|
|
10
10
|
logoutSession,
|
|
11
11
|
jwtSign,
|
|
12
|
-
getBearerToken,
|
|
13
12
|
validatePasswordMiddleware,
|
|
14
13
|
} from '../../server/auth.js';
|
|
15
14
|
import { MailerProvider } from '../../mailer/MailerProvider.js';
|
|
@@ -19,8 +18,8 @@ import validator from 'validator';
|
|
|
19
18
|
import { DataBaseProvider } from '../../db/DataBaseProvider.js';
|
|
20
19
|
import { FileFactory, FileCleanup } from '../file/file.service.js';
|
|
21
20
|
import { UserDto } from './user.model.js';
|
|
22
|
-
import { selectDtoFactory, ValkeyAPI } from '../../server/valkey.js';
|
|
23
21
|
import { timer } from '../../client/components/core/CommonJs.js';
|
|
22
|
+
import { GuestService } from './guest.service.js';
|
|
24
23
|
|
|
25
24
|
const logger = loggerFactory(import.meta);
|
|
26
25
|
|
|
@@ -226,15 +225,7 @@ const UserService = {
|
|
|
226
225
|
} else throw new Error('Invalid credentials');
|
|
227
226
|
|
|
228
227
|
case 'guest': {
|
|
229
|
-
|
|
230
|
-
await ValkeyAPI.setValkeyObject(options, user.email, user);
|
|
231
|
-
return {
|
|
232
|
-
token: jwtSign(
|
|
233
|
-
UserDto.auth.payload(user, null, req.ip, req.headers['user-agent'], options.host, options.path),
|
|
234
|
-
options,
|
|
235
|
-
),
|
|
236
|
-
user: selectDtoFactory(user, UserDto.select.get()),
|
|
237
|
-
};
|
|
228
|
+
return await GuestService.create(req, options);
|
|
238
229
|
}
|
|
239
230
|
|
|
240
231
|
default:
|
|
@@ -360,8 +351,7 @@ const UserService = {
|
|
|
360
351
|
case 'auth': {
|
|
361
352
|
let user;
|
|
362
353
|
if (req.auth.user._id.match('guest')) {
|
|
363
|
-
|
|
364
|
-
if (!user) throw new Error('guest user expired');
|
|
354
|
+
return await GuestService.auth(req, options);
|
|
365
355
|
} else
|
|
366
356
|
user = await User.findOne({
|
|
367
357
|
_id: req.auth.user._id,
|
|
@@ -369,13 +359,6 @@ const UserService = {
|
|
|
369
359
|
|
|
370
360
|
if (!user) throw new Error('user not found');
|
|
371
361
|
|
|
372
|
-
const guestUser = await ValkeyAPI.getValkeyObject(options, req.auth.user.email);
|
|
373
|
-
if (guestUser)
|
|
374
|
-
return {
|
|
375
|
-
user: selectDtoFactory(guestUser, UserDto.select.get()),
|
|
376
|
-
token: getBearerToken(req),
|
|
377
|
-
};
|
|
378
|
-
|
|
379
362
|
return {
|
|
380
363
|
token: await refreshSessionAndToken(req, res, User, options),
|
|
381
364
|
user: await User.findOne({
|
package/src/cli/deploy.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
buildPortProxyRouter,
|
|
10
10
|
buildProxyRouter,
|
|
11
11
|
Config,
|
|
12
|
+
cronDeployIdResolve,
|
|
12
13
|
deployRangePortFactory,
|
|
13
14
|
getDataDeploy,
|
|
14
15
|
loadConfServerJson,
|
|
@@ -130,8 +131,8 @@ class UnderpostDeploy {
|
|
|
130
131
|
deploymentYamlPartsFactory({ deployId, env, suffix, resources, replicas, image, namespace, volumes, cmd }) {
|
|
131
132
|
if (!cmd)
|
|
132
133
|
cmd = [
|
|
133
|
-
`npm install -g npm@11.2.0`,
|
|
134
|
-
`npm install -g underpost`,
|
|
134
|
+
// `npm install -g npm@11.2.0`,
|
|
135
|
+
// `npm install -g underpost`,
|
|
135
136
|
`underpost secret underpost --create-from-env`,
|
|
136
137
|
`underpost start --build --run ${deployId} ${env}`,
|
|
137
138
|
];
|
|
@@ -141,7 +142,8 @@ class UnderpostDeploy {
|
|
|
141
142
|
? JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.volume.json`, 'utf8'))
|
|
142
143
|
: [];
|
|
143
144
|
volumes = volumes.concat(confVolume);
|
|
144
|
-
const containerImage = image ? image : `localhost/rockylinux9-underpost:v${packageJson.version}`;
|
|
145
|
+
// const containerImage = image ? image : `localhost/rockylinux9-underpost:v${packageJson.version}`;
|
|
146
|
+
const containerImage = image ? image : `underpost/underpost-engine:v${packageJson.version}`;
|
|
145
147
|
return `apiVersion: apps/v1
|
|
146
148
|
kind: Deployment
|
|
147
149
|
metadata:
|
|
@@ -164,6 +166,7 @@ spec:
|
|
|
164
166
|
containers:
|
|
165
167
|
- name: ${deployId}-${env}-${suffix}
|
|
166
168
|
image: ${containerImage}
|
|
169
|
+
imagePullPolicy: ${containerImage.startsWith('localhost/') ? 'Never' : 'IfNotPresent'}
|
|
167
170
|
envFrom:
|
|
168
171
|
- secretRef:
|
|
169
172
|
name: underpost-config
|
|
@@ -624,6 +627,8 @@ EOF`);
|
|
|
624
627
|
path: instance.path,
|
|
625
628
|
fromPort: instance.fromPort,
|
|
626
629
|
toPort: instance.toPort,
|
|
630
|
+
fromDebugPort: instance.fromDebugPort,
|
|
631
|
+
toDebugPort: instance.toDebugPort,
|
|
627
632
|
traffic: Underpost.deploy.getCurrentTraffic(_deployId, { namespace, hostTest: instance.host }),
|
|
628
633
|
});
|
|
629
634
|
}
|
|
@@ -801,9 +806,10 @@ EOF`);
|
|
|
801
806
|
* @memberof UnderpostDeploy
|
|
802
807
|
*/
|
|
803
808
|
configMap(env, namespace = 'default') {
|
|
809
|
+
const cronDeployId = cronDeployIdResolve() || 'dd-cron';
|
|
804
810
|
shellExec(`kubectl delete secret underpost-config -n ${namespace} --ignore-not-found`);
|
|
805
811
|
shellExec(
|
|
806
|
-
`kubectl create secret generic underpost-config --from-env-file=/home/dd/engine/engine-private/conf
|
|
812
|
+
`kubectl create secret generic underpost-config --from-env-file=/home/dd/engine/engine-private/conf/${cronDeployId}/.env.${env} --dry-run=client -o yaml | kubectl apply -f - -n ${namespace}`,
|
|
807
813
|
);
|
|
808
814
|
},
|
|
809
815
|
/**
|
|
@@ -1092,11 +1098,10 @@ ${renderHosts}`,
|
|
|
1092
1098
|
* @param {string} targetTraffic - Target traffic status for the deployment.
|
|
1093
1099
|
* @param {Array<string>} ignorePods - List of pod names to ignore.
|
|
1094
1100
|
* @param {string} [namespace='default'] - Kubernetes namespace for the deployment.
|
|
1095
|
-
* @param {string} [outLogType=''] - Type of log output.
|
|
1096
1101
|
* @returns {object} - Object containing the ready status of the deployment.
|
|
1097
1102
|
* @memberof UnderpostDeploy
|
|
1098
1103
|
*/
|
|
1099
|
-
async monitorReadyRunner(deployId, env, targetTraffic, ignorePods = [], namespace = 'default'
|
|
1104
|
+
async monitorReadyRunner(deployId, env, targetTraffic, ignorePods = [], namespace = 'default') {
|
|
1100
1105
|
let checkStatusIteration = 0;
|
|
1101
1106
|
const checkStatusIterationMsDelay = 1000;
|
|
1102
1107
|
const maxIterations = 3000;
|
|
@@ -1134,32 +1139,30 @@ ${renderHosts}`,
|
|
|
1134
1139
|
}
|
|
1135
1140
|
}
|
|
1136
1141
|
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
const { NAME, out } = pod;
|
|
1142
|
+
{
|
|
1143
|
+
let indexOf = -1;
|
|
1144
|
+
for (const pod of result.notReadyPods) {
|
|
1145
|
+
indexOf++;
|
|
1146
|
+
const { NAME, out } = pod;
|
|
1143
1147
|
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1148
|
+
if (out.match('not') && out.match('found') && checkStatusIteration <= 20 && out.match(deploymentId))
|
|
1149
|
+
lastMsg[NAME] = 'Starting deployment';
|
|
1150
|
+
// else if (out.match('not') && out.match('found') && checkStatusIteration <= 20 && out.match('underpost'))
|
|
1151
|
+
// lastMsg[NAME] = 'Installing underpost cli';
|
|
1152
|
+
else if (out.match('not') && out.match('found') && checkStatusIteration <= 20 && out.match('task'))
|
|
1153
|
+
lastMsg[NAME] = 'Initializing setup task';
|
|
1154
|
+
else if (out.match('Empty environment variables')) lastMsg[NAME] = 'Setup environment';
|
|
1155
|
+
else if (out.match(`${deployId}-${env}-build-deployment`)) lastMsg[NAME] = 'Building apps/services';
|
|
1156
|
+
else if (out.match(`${deployId}-${env}-initializing-deployment`))
|
|
1157
|
+
lastMsg[NAME] = 'Initializing apps/services';
|
|
1158
|
+
else if (!lastMsg[NAME]) lastMsg[NAME] = `Waiting for status`;
|
|
1155
1159
|
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
}
|
|
1160
|
+
console.log(
|
|
1161
|
+
'Target pod:',
|
|
1162
|
+
NAME[NAME.match('green') ? 'bgGreen' : 'bgBlue'].bold.black,
|
|
1163
|
+
'| Status:',
|
|
1164
|
+
lastMsg[NAME].bold.magenta,
|
|
1165
|
+
);
|
|
1163
1166
|
}
|
|
1164
1167
|
}
|
|
1165
1168
|
await timer(checkStatusIterationMsDelay);
|
package/src/cli/fs.js
CHANGED
|
@@ -75,6 +75,7 @@ class UnderpostFileStorage {
|
|
|
75
75
|
* @param {boolean} [options.force=false] - Flag to force file operations.
|
|
76
76
|
* @param {boolean} [options.pull=false] - Flag to pull files from storage.
|
|
77
77
|
* @param {boolean} [options.git=false] - Flag to use Git for file operations.
|
|
78
|
+
* @param {boolean} [options.omitUnzip=false] - If true, do not extract zip and keep downloaded zip file.
|
|
78
79
|
* @param {string} [options.storageFilePath=''] - The path to the storage configuration file.
|
|
79
80
|
* @returns {Promise<void>} A promise that resolves when the recursive callback is complete.
|
|
80
81
|
* @memberof UnderpostFileStorage
|
|
@@ -88,10 +89,50 @@ class UnderpostFileStorage {
|
|
|
88
89
|
force: false,
|
|
89
90
|
pull: false,
|
|
90
91
|
git: false,
|
|
92
|
+
omitUnzip: false,
|
|
91
93
|
storageFilePath: '',
|
|
92
94
|
},
|
|
93
95
|
) {
|
|
94
96
|
const { storage, storageConf } = Underpost.fs.getStorageConf(options);
|
|
97
|
+
|
|
98
|
+
// In recursive remove mode, delete every tracked storage key under the requested path,
|
|
99
|
+
// even when local files/directories are already missing.
|
|
100
|
+
if (options.rm === true) {
|
|
101
|
+
const normalizedPath = typeof path === 'string' ? path.trim() : '';
|
|
102
|
+
const basePath = normalizedPath.replace(/\/+$/, '');
|
|
103
|
+
const hasPathFilter = basePath.length > 0;
|
|
104
|
+
|
|
105
|
+
const associatedPaths = Object.keys(storage || {}).filter((storedPath) => {
|
|
106
|
+
if (!hasPathFilter) return true;
|
|
107
|
+
return storedPath === basePath || storedPath.startsWith(`${basePath}/`);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
for (const associatedPath of associatedPaths) {
|
|
111
|
+
await Underpost.fs.delete(associatedPath);
|
|
112
|
+
if (storage) delete storage[associatedPath];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (hasPathFilter && options.force === true && fs.existsSync(basePath)) fs.removeSync(basePath);
|
|
116
|
+
|
|
117
|
+
Underpost.fs.writeStorageConf(storage, storageConf);
|
|
118
|
+
|
|
119
|
+
if (associatedPaths.length === 0)
|
|
120
|
+
logger.warn('No associated tracked storage paths found', { path: hasPathFilter ? basePath : '*' });
|
|
121
|
+
else
|
|
122
|
+
logger.info('Removed associated tracked storage paths', {
|
|
123
|
+
path: hasPathFilter ? basePath : '*',
|
|
124
|
+
removed: associatedPaths.length,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (options.git === true) {
|
|
128
|
+
const gitPath = hasPathFilter ? basePath : '.';
|
|
129
|
+
shellExec(`cd ${gitPath} && git add .`);
|
|
130
|
+
shellExec(`underpost cmt ${gitPath} feat`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
95
136
|
const deleteFiles = options.pull === true ? [] : Underpost.repo.getDeleteFiles(path);
|
|
96
137
|
for (const relativePath of deleteFiles) {
|
|
97
138
|
const _path = path + '/' + relativePath;
|
|
@@ -143,12 +184,13 @@ class UnderpostFileStorage {
|
|
|
143
184
|
* @param {boolean} [options.force=false] - Flag to force file operations.
|
|
144
185
|
* @param {boolean} [options.pull=false] - Flag to pull files from storage.
|
|
145
186
|
* @param {boolean} [options.git=false] - Flag to use Git for file operations.
|
|
187
|
+
* @param {boolean} [options.omitUnzip=false] - If true, do not extract zip and keep downloaded zip file.
|
|
146
188
|
* @returns {Promise<void>} A promise that resolves when the callback is complete.
|
|
147
189
|
* @memberof UnderpostFileStorage
|
|
148
190
|
*/
|
|
149
191
|
async callback(
|
|
150
192
|
path,
|
|
151
|
-
options = { rm: false, recursive: false, deployId: '', force: false, pull: false, git: false },
|
|
193
|
+
options = { rm: false, recursive: false, deployId: '', force: false, pull: false, git: false, omitUnzip: false },
|
|
152
194
|
) {
|
|
153
195
|
if (options.recursive === true || options.git === true)
|
|
154
196
|
return await Underpost.fs.recursiveCallback(path, options);
|
|
@@ -191,21 +233,36 @@ class UnderpostFileStorage {
|
|
|
191
233
|
* @method pull
|
|
192
234
|
* @description Pulls a file from Cloudinary.
|
|
193
235
|
* @param {string} path - The path to the file to pull.
|
|
236
|
+
* @param {object} [options] - Pull options.
|
|
237
|
+
* @param {boolean} [options.omitUnzip=false] - If true, do not extract zip and keep downloaded zip file.
|
|
194
238
|
* @returns {Promise<void>} A promise that resolves when the file is pulled.
|
|
195
239
|
* @memberof UnderpostFileStorage
|
|
196
240
|
*/
|
|
197
|
-
async pull(path) {
|
|
241
|
+
async pull(path, options = { omitUnzip: false, force: false }) {
|
|
198
242
|
Underpost.fs.cloudinaryConfig();
|
|
199
243
|
const folder = dir.dirname(path);
|
|
200
244
|
if (!fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true });
|
|
245
|
+
const zipPath = `${path}.zip`;
|
|
246
|
+
|
|
247
|
+
if (options.omitUnzip === true && options.force !== true && fs.existsSync(zipPath)) {
|
|
248
|
+
logger.warn('pull skipped, zip already exists and omit-unzip is enabled', { path, zipPath });
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
201
252
|
const downloadResult = await cloudinary.utils.download_archive_url({
|
|
202
253
|
public_ids: [path],
|
|
203
254
|
resource_type: 'raw',
|
|
204
255
|
});
|
|
205
256
|
logger.info('download result', downloadResult);
|
|
206
|
-
await Downloader.downloadFile(downloadResult,
|
|
207
|
-
|
|
208
|
-
|
|
257
|
+
await Downloader.downloadFile(downloadResult, zipPath);
|
|
258
|
+
|
|
259
|
+
if (options.omitUnzip === true) {
|
|
260
|
+
logger.warn('omit unzip enabled, keeping downloaded zip file', { path, zipPath });
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
path = Underpost.fs.zip2File(zipPath);
|
|
265
|
+
fs.removeSync(`${path}.zip`);
|
|
209
266
|
},
|
|
210
267
|
async delete(path) {
|
|
211
268
|
Underpost.fs.cloudinaryConfig();
|
package/src/cli/image.js
CHANGED
|
@@ -99,6 +99,7 @@ class UnderpostImage {
|
|
|
99
99
|
if (!path) path = '.';
|
|
100
100
|
if (!imageName) imageName = `rockylinux9-underpost:${Underpost.version}`;
|
|
101
101
|
if (!imagePath) imagePath = '.';
|
|
102
|
+
if (imageName.match('/')) imageName = imageName.split('/')[1];
|
|
102
103
|
if (!version) version = 'latest';
|
|
103
104
|
version = imageName && imageName.match(':') ? '' : `:${version}`;
|
|
104
105
|
const podManImg = `localhost/${imageName}${version}`;
|
|
@@ -178,6 +179,12 @@ class UnderpostImage {
|
|
|
178
179
|
* @memberof UnderpostImage
|
|
179
180
|
*/
|
|
180
181
|
pullDockerHubImage(options = { k3s: false, kubeadm: false, kind: false, dockerhubImage: '', version: '' }) {
|
|
182
|
+
if (options.dockerhubImage && options.dockerhubImage.startsWith('localhost')) {
|
|
183
|
+
logger.warn(`[image] pullDockerHubImage skipped — local image cannot be pulled from Docker Hub`, {
|
|
184
|
+
dockerhubImage: options.dockerhubImage,
|
|
185
|
+
});
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
181
188
|
if (options.dockerhubImage === 'underpost') {
|
|
182
189
|
options.dockerhubImage = 'underpost/underpost-engine';
|
|
183
190
|
if (!options.version) options.version = Underpost.version;
|
|
@@ -185,7 +192,42 @@ class UnderpostImage {
|
|
|
185
192
|
if (!options.version) options.version = 'latest';
|
|
186
193
|
const version = options.dockerhubImage && options.dockerhubImage.match(':') ? '' : `:${options.version}`;
|
|
187
194
|
const image = `${options.dockerhubImage}${version}`;
|
|
188
|
-
|
|
195
|
+
const targetKind = options.kind === true;
|
|
196
|
+
const targetK3s = options.k3s === true;
|
|
197
|
+
const targetKubeadm = options.kubeadm === true || (!targetKind && !targetK3s);
|
|
198
|
+
|
|
199
|
+
const requestedRepo = image.replace(/:[^/]+$/, '');
|
|
200
|
+
const requestedTag = image.match(/:([^/]+)$/)?.[1] || 'latest';
|
|
201
|
+
const normalizeRepo = (repo = '') =>
|
|
202
|
+
repo
|
|
203
|
+
.trim()
|
|
204
|
+
.replace(/^localhost\//, '')
|
|
205
|
+
.replace(/^docker\.io\//, '')
|
|
206
|
+
.replace(/^library\//, '');
|
|
207
|
+
|
|
208
|
+
const currentImages = UnderpostImage.API.list({
|
|
209
|
+
kind: targetKind,
|
|
210
|
+
kubeadm: targetKubeadm,
|
|
211
|
+
k3s: targetK3s,
|
|
212
|
+
log: false,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const existsInCluster = currentImages.some((row) => {
|
|
216
|
+
const rowImageRaw = String(row.IMAGE || row.image || '').trim();
|
|
217
|
+
if (!rowImageRaw) return false;
|
|
218
|
+
const rowImage = rowImageRaw.replace(/:[^/]+$/, '');
|
|
219
|
+
const rowTag = String(row.TAG || rowImageRaw.match(/:([^/]+)$/)?.[1] || '').trim();
|
|
220
|
+
return normalizeRepo(rowImage) === normalizeRepo(requestedRepo) && rowTag === requestedTag;
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
if (existsInCluster) {
|
|
224
|
+
logger.info(`[image] pull skipped. Image already loaded`, {
|
|
225
|
+
image,
|
|
226
|
+
clusterType: targetKind ? 'kind' : targetK3s ? 'k3s' : 'kubeadm',
|
|
227
|
+
});
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (targetKind) {
|
|
189
231
|
shellExec(`docker pull ${image}`);
|
|
190
232
|
shellExec(`sudo kind load docker-image ${image}`);
|
|
191
233
|
} else {
|
package/src/cli/index.js
CHANGED
|
@@ -46,6 +46,9 @@ program
|
|
|
46
46
|
.option('--sync-env-port', 'Sync environment port assignments across all deploy IDs')
|
|
47
47
|
.option('--single-replica', 'Build single replica folders instead of full client')
|
|
48
48
|
.option('--build-zip', 'Create zip files of the builds')
|
|
49
|
+
.option('--split <mb>', 'Split generated zip files into parts of the specified size in MB')
|
|
50
|
+
.option('--unzip <build-prefix>', 'Extract a built client zip or split zip parts using the given build prefix')
|
|
51
|
+
.option('--merge-zip <build-prefix>', 'Merge split ZIP parts back into a single ZIP file for the given build prefix')
|
|
49
52
|
.option('--lite-build', 'Skip full build (default is full build)')
|
|
50
53
|
.option('--icons-build', 'Build icons')
|
|
51
54
|
.description('Builds client assets, single replicas, and/or syncs environment ports.')
|
|
@@ -62,6 +65,7 @@ program
|
|
|
62
65
|
.option('--build', 'Triggers the client-side application build process.')
|
|
63
66
|
.option('--underpost-quickly-install', 'Uses Underpost Quickly Install for dependency installation.')
|
|
64
67
|
.option('--skip-pull-base', 'Skips cloning repositories, uses current workspace code directly.')
|
|
68
|
+
.option('--skip-full-build', 'Skips the full client bundle build during deployment.')
|
|
65
69
|
.action(Underpost.start.callback)
|
|
66
70
|
.description('Initiates application servers, build pipelines, or other defined services based on the deployment ID.');
|
|
67
71
|
|
|
@@ -485,6 +489,7 @@ program
|
|
|
485
489
|
.option('--recursive', 'Uploads files recursively from the specified path.')
|
|
486
490
|
.option('--deploy-id <deploy-id>', 'Specifies the deployment configuration ID for file operations.')
|
|
487
491
|
.option('--pull', 'Downloads the specified file.')
|
|
492
|
+
.option('--omit-unzip', 'With --pull, keeps the downloaded .zip file and skips extraction.')
|
|
488
493
|
.option('--force', 'Forces the action, overriding any warnings or conflicts.')
|
|
489
494
|
.option('--storage-file-path <storage-file-path>', 'Specifies a custom file storage path.')
|
|
490
495
|
.description('Manages file storage, defaulting to file upload operations.')
|
|
@@ -613,7 +618,6 @@ program
|
|
|
613
618
|
.option('--k3s', 'Sets the k3s cluster context for the runner execution.')
|
|
614
619
|
.option('--kind', 'Sets the kind cluster context for the runner execution.')
|
|
615
620
|
.option('--git-clean', 'Runs git clean on volume mount paths before copying.')
|
|
616
|
-
.option('--log-type <log-type>', 'Sets the log type for the runner execution.')
|
|
617
621
|
.option('--deploy-id <deploy-id>', 'Sets deploy id context for the runner execution.')
|
|
618
622
|
.option('--user <user>', 'Sets user context for the runner execution.')
|
|
619
623
|
.option('--hosts <hosts>', 'Comma-separated list of hosts for the runner execution.')
|
package/src/cli/release.js
CHANGED
|
@@ -120,7 +120,7 @@ class UnderpostRelease {
|
|
|
120
120
|
`./manifests/deployment/dd-default-development/deployment.yaml`,
|
|
121
121
|
fs
|
|
122
122
|
.readFileSync(`./manifests/deployment/dd-default-development/deployment.yaml`, 'utf8')
|
|
123
|
-
.replaceAll(`underpost:v${version}`, `underpost:v${newVersion}`),
|
|
123
|
+
.replaceAll(`underpost-engine:v${version}`, `underpost-engine:v${newVersion}`),
|
|
124
124
|
'utf8',
|
|
125
125
|
);
|
|
126
126
|
|
|
@@ -133,6 +133,62 @@ class UnderpostRelease {
|
|
|
133
133
|
'utf8',
|
|
134
134
|
);
|
|
135
135
|
|
|
136
|
+
// Update underpost/* image versions in all engine-*.cd.yml workflows.
|
|
137
|
+
for (const wf of fs.readdirSync(`./.github/workflows`)) {
|
|
138
|
+
if (!wf.match(/^engine-.+\.cd\.yml$/)) continue;
|
|
139
|
+
const wfPath = `./.github/workflows/${wf}`;
|
|
140
|
+
const updated = fs
|
|
141
|
+
.readFileSync(wfPath, 'utf8')
|
|
142
|
+
.replace(/underpost\/([^:'"]+):v[0-9]+\.[0-9]+\.[0-9]+/g, `underpost/$1:v${newVersion}`);
|
|
143
|
+
fs.writeFileSync(wfPath, updated, 'utf8');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Update version tag in all runtime docker image workflows (type=raw,value=v<version>).
|
|
147
|
+
for (const wf of fs.readdirSync(`./.github/workflows`)) {
|
|
148
|
+
if (!wf.match(/^docker-image\..+\.ci\.yml$/) || wf === 'docker-image.ci.yml') continue;
|
|
149
|
+
const wfPath = `./.github/workflows/${wf}`;
|
|
150
|
+
fs.writeFileSync(
|
|
151
|
+
wfPath,
|
|
152
|
+
fs.readFileSync(wfPath, 'utf8').replaceAll(`type=raw,value=v${version}`, `type=raw,value=v${newVersion}`),
|
|
153
|
+
'utf8',
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Update image version in all conf.instances.json files for underpost/* images.
|
|
158
|
+
if (fs.existsSync(`./engine-private/conf`)) {
|
|
159
|
+
const confFiles = await fs.readdir(`./engine-private/conf`, { recursive: true });
|
|
160
|
+
for (const relativePath of confFiles) {
|
|
161
|
+
const filePath = `./engine-private/conf/${relativePath.replaceAll('\\', '/')}`;
|
|
162
|
+
if (filePath.split('/').pop() !== 'conf.instances.json' || !fs.existsSync(filePath)) continue;
|
|
163
|
+
|
|
164
|
+
let instances;
|
|
165
|
+
try {
|
|
166
|
+
instances = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
167
|
+
} catch {
|
|
168
|
+
logger.warn(`Skipping invalid JSON file: ${filePath}`);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!Array.isArray(instances)) continue;
|
|
173
|
+
|
|
174
|
+
let updated = false;
|
|
175
|
+
for (const instance of instances) {
|
|
176
|
+
if (!instance || typeof instance !== 'object') continue;
|
|
177
|
+
if (!instance.image || typeof instance.image !== 'string') continue;
|
|
178
|
+
if (!instance.image.startsWith('underpost/')) continue;
|
|
179
|
+
|
|
180
|
+
const baseImage = instance.image.split('@')[0].split(':')[0];
|
|
181
|
+
const nextImage = `${baseImage}:v${newVersion}`;
|
|
182
|
+
if (instance.image !== nextImage) {
|
|
183
|
+
instance.image = nextImage;
|
|
184
|
+
updated = true;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (updated) fs.writeFileSync(filePath, JSON.stringify(instances, null, 2), 'utf8');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
136
192
|
fs.writeFileSync(
|
|
137
193
|
`./src/index.js`,
|
|
138
194
|
fs.readFileSync(`./src/index.js`, 'utf8').replaceAll(`${version}`, `${newVersion}`),
|
package/src/cli/repository.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
buildReplicaId,
|
|
22
22
|
writeEnv,
|
|
23
23
|
} from '../server/conf.js';
|
|
24
|
-
import { buildClient } from '../server/client-build.js';
|
|
24
|
+
import { buildClient, unzipClientBuild, mergeClientBuildZip } from '../server/client-build.js';
|
|
25
25
|
import { DefaultConf } from '../../conf.js';
|
|
26
26
|
import Underpost from '../index.js';
|
|
27
27
|
|
|
@@ -642,6 +642,9 @@ class UnderpostRepository {
|
|
|
642
642
|
* @param {boolean} [options.syncEnvPort=false] - If true, syncs environment port assignments across all deploy IDs.
|
|
643
643
|
* @param {boolean} [options.singleReplica=false] - If true, builds single replica folders instead of full client.
|
|
644
644
|
* @param {boolean} [options.buildZip=false] - If true, creates zip files of the builds.
|
|
645
|
+
* @param {string|number} [options.split=''] - Optional ZIP part size in MB. When set with buildZip, writes split parts.
|
|
646
|
+
* @param {string} [options.unzip=''] - Optional build ZIP prefix to extract from ./build.
|
|
647
|
+
* @param {string} [options.mergeZip=''] - Optional build prefix to merge split ZIP parts into a single ZIP.
|
|
645
648
|
* @param {boolean} [options.liteBuild=false] - If true, skips full build (default is full build).
|
|
646
649
|
* @param {boolean} [options.iconsBuild=false] - If true, builds icons.
|
|
647
650
|
* @returns {Promise<boolean>} A promise that resolves when the build is complete.
|
|
@@ -656,12 +659,31 @@ class UnderpostRepository {
|
|
|
656
659
|
syncEnvPort: false,
|
|
657
660
|
singleReplica: false,
|
|
658
661
|
buildZip: false,
|
|
662
|
+
split: '',
|
|
663
|
+
unzip: '',
|
|
664
|
+
mergeZip: '',
|
|
659
665
|
liteBuild: false,
|
|
660
666
|
iconsBuild: false,
|
|
661
667
|
},
|
|
662
668
|
) {
|
|
663
669
|
return new Promise(async (resolve, reject) => {
|
|
664
670
|
try {
|
|
671
|
+
if (options.mergeZip) {
|
|
672
|
+
mergeClientBuildZip({
|
|
673
|
+
buildPrefix: options.mergeZip,
|
|
674
|
+
logger,
|
|
675
|
+
});
|
|
676
|
+
return resolve(true);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (options.unzip) {
|
|
680
|
+
unzipClientBuild({
|
|
681
|
+
buildPrefix: options.unzip,
|
|
682
|
+
logger,
|
|
683
|
+
});
|
|
684
|
+
return resolve(true);
|
|
685
|
+
}
|
|
686
|
+
|
|
665
687
|
// Handle singleReplica operation (must run before syncEnvPort to ensure replica dirs exist)
|
|
666
688
|
if (options.singleReplica) {
|
|
667
689
|
const replicaPath = path;
|
|
@@ -804,6 +826,9 @@ class UnderpostRepository {
|
|
|
804
826
|
let argPath = path ? path.split(',') : [];
|
|
805
827
|
let deployIdSingleReplicas = [];
|
|
806
828
|
let singleReplicaHosts = [];
|
|
829
|
+
const isReplicaContext = resolvedDeployId
|
|
830
|
+
? fs.existsSync(`./engine-private/replica/${resolvedDeployId}`)
|
|
831
|
+
: false;
|
|
807
832
|
const serverConf = resolvedDeployId
|
|
808
833
|
? readConfJson(resolvedDeployId, 'server', { loadReplicas: true })
|
|
809
834
|
: Config.default.server;
|
|
@@ -814,7 +839,7 @@ class UnderpostRepository {
|
|
|
814
839
|
if (argHost.length && argPath.length && (!argHost.includes(host) || !argPath.includes(path))) {
|
|
815
840
|
delete serverConf[host][path];
|
|
816
841
|
} else {
|
|
817
|
-
if (serverConf[host][path].singleReplica && serverConf[host][path].replicas) {
|
|
842
|
+
if (!isReplicaContext && serverConf[host][path].singleReplica && serverConf[host][path].replicas) {
|
|
818
843
|
singleReplicaHosts.push({ host, path });
|
|
819
844
|
deployIdSingleReplicas = deployIdSingleReplicas.concat(
|
|
820
845
|
serverConf[host][path].replicas.map((replica) =>
|
|
@@ -827,10 +852,17 @@ class UnderpostRepository {
|
|
|
827
852
|
}
|
|
828
853
|
await buildClient({
|
|
829
854
|
buildZip: options.buildZip || false,
|
|
855
|
+
split: options.split || '',
|
|
830
856
|
fullBuild: options.liteBuild ? false : true,
|
|
831
857
|
iconsBuild: options.iconsBuild || false,
|
|
832
858
|
});
|
|
833
|
-
for (const replicaDeployId of deployIdSingleReplicas)
|
|
859
|
+
for (const replicaDeployId of deployIdSingleReplicas) {
|
|
860
|
+
if (!fs.existsSync(`./engine-private/replica/${replicaDeployId}`)) {
|
|
861
|
+
logger.warn('Skip replica client build: replica folder not found', { replicaDeployId });
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
await Underpost.repo.client(replicaDeployId);
|
|
865
|
+
}
|
|
834
866
|
|
|
835
867
|
return resolve(true);
|
|
836
868
|
}
|