@magentrix-corp/magentrix-cli 1.3.16 → 1.3.17
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/LICENSE +25 -25
- package/README.md +1166 -1166
- package/actions/autopublish.old.js +293 -293
- package/actions/config.js +182 -182
- package/actions/create.js +466 -466
- package/actions/help.js +164 -164
- package/actions/iris/buildStage.js +874 -874
- package/actions/iris/delete.js +256 -256
- package/actions/iris/dev.js +391 -391
- package/actions/iris/index.js +6 -6
- package/actions/iris/link.js +375 -375
- package/actions/iris/recover.js +268 -268
- package/actions/main.js +80 -80
- package/actions/publish.js +1420 -1420
- package/actions/pull.js +684 -684
- package/actions/setup.js +148 -148
- package/actions/status.js +17 -17
- package/actions/update.js +248 -248
- package/bin/magentrix.js +393 -393
- package/package.json +55 -55
- package/utils/assetPaths.js +158 -158
- package/utils/autopublishLock.js +77 -77
- package/utils/cacher.js +206 -206
- package/utils/cli/checkInstanceUrl.js +76 -74
- package/utils/cli/helpers/compare.js +282 -282
- package/utils/cli/helpers/ensureApiKey.js +63 -63
- package/utils/cli/helpers/ensureCredentials.js +68 -68
- package/utils/cli/helpers/ensureInstanceUrl.js +75 -75
- package/utils/cli/writeRecords.js +262 -262
- package/utils/compare.js +135 -135
- package/utils/compress.js +17 -17
- package/utils/config.js +527 -527
- package/utils/debug.js +144 -144
- package/utils/diagnostics/testPublishLogic.js +96 -96
- package/utils/diff.js +49 -49
- package/utils/downloadAssets.js +291 -291
- package/utils/filetag.js +115 -115
- package/utils/hash.js +14 -14
- package/utils/iris/backup.js +411 -411
- package/utils/iris/builder.js +541 -541
- package/utils/iris/config-reader.js +664 -664
- package/utils/iris/deleteHelper.js +150 -150
- package/utils/iris/errors.js +537 -537
- package/utils/iris/linker.js +601 -601
- package/utils/iris/lock.js +360 -360
- package/utils/iris/validation.js +360 -360
- package/utils/iris/validator.js +281 -281
- package/utils/iris/zipper.js +248 -248
- package/utils/logger.js +291 -291
- package/utils/magentrix/api/assets.js +220 -220
- package/utils/magentrix/api/auth.js +107 -107
- package/utils/magentrix/api/createEntity.js +61 -61
- package/utils/magentrix/api/deleteEntity.js +55 -55
- package/utils/magentrix/api/iris.js +251 -251
- package/utils/magentrix/api/meqlQuery.js +36 -36
- package/utils/magentrix/api/retrieveEntity.js +86 -86
- package/utils/magentrix/api/updateEntity.js +66 -66
- package/utils/magentrix/fetch.js +168 -168
- package/utils/merge.js +22 -22
- package/utils/permissionError.js +70 -70
- package/utils/preferences.js +40 -40
- package/utils/progress.js +469 -469
- package/utils/spinner.js +43 -43
- package/utils/template.js +52 -52
- package/utils/updateFileBase.js +121 -121
- package/utils/workspaces.js +108 -108
- package/vars/config.js +11 -11
- package/vars/global.js +50 -50
package/actions/iris/delete.js
CHANGED
|
@@ -1,256 +1,256 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { select, input, confirm } from '@inquirer/prompts';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import Config from '../../utils/config.js';
|
|
5
|
-
import { ensureValidCredentials } from '../../utils/cli/helpers/ensureCredentials.js';
|
|
6
|
-
import { backupIrisApp } from '../../utils/iris/backup.js';
|
|
7
|
-
import { getLinkedProjects, unlinkVueProject } from '../../utils/iris/linker.js';
|
|
8
|
-
import { deleteIrisAppFromServer, deleteLocalIrisAppFiles } from '../../utils/iris/deleteHelper.js';
|
|
9
|
-
import { showPermissionError } from '../../utils/permissionError.js';
|
|
10
|
-
import { EXPORT_ROOT, IRIS_APPS_DIR } from '../../vars/global.js';
|
|
11
|
-
import { acquireLock, releaseLock, LockTypes } from '../../utils/iris/lock.js';
|
|
12
|
-
import { formatFileLockError } from '../../utils/iris/errors.js';
|
|
13
|
-
|
|
14
|
-
const config = new Config();
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* iris-app-delete command - Delete a published Iris app with backup and recovery.
|
|
18
|
-
*/
|
|
19
|
-
export const irisDelete = async () => {
|
|
20
|
-
process.stdout.write('\x1Bc'); // Clear console
|
|
21
|
-
|
|
22
|
-
console.log(chalk.red.bold('\n⚠ Delete Iris App'));
|
|
23
|
-
console.log(chalk.gray('─'.repeat(48)));
|
|
24
|
-
console.log();
|
|
25
|
-
|
|
26
|
-
// Get list of published apps from base.json
|
|
27
|
-
const cachedResults = config.read(null, { filename: "base.json" });
|
|
28
|
-
const cachedIrisApps = Object.values(cachedResults || {})
|
|
29
|
-
.filter(entry => entry.type === 'IrisApp' || entry.Type === 'IrisApp')
|
|
30
|
-
.map(entry => {
|
|
31
|
-
const slug = entry.folderName || (entry.recordId && entry.recordId.startsWith('iris-app:')
|
|
32
|
-
? entry.recordId.replace('iris-app:', '')
|
|
33
|
-
: null);
|
|
34
|
-
return {
|
|
35
|
-
slug,
|
|
36
|
-
appName: entry.appName || slug,
|
|
37
|
-
folderName: entry.folderName || slug
|
|
38
|
-
};
|
|
39
|
-
})
|
|
40
|
-
.filter(app => app.slug);
|
|
41
|
-
|
|
42
|
-
if (cachedIrisApps.length === 0) {
|
|
43
|
-
console.log(chalk.yellow('No published Iris apps found.'));
|
|
44
|
-
console.log();
|
|
45
|
-
console.log(chalk.gray('Published apps appear here after running:'));
|
|
46
|
-
console.log(chalk.white(` 1. ${chalk.cyan('magentrix vue-run-build')}`));
|
|
47
|
-
console.log(chalk.white(` 2. ${chalk.cyan('magentrix publish')}`));
|
|
48
|
-
console.log();
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Build choices
|
|
53
|
-
const choices = cachedIrisApps.map(app => ({
|
|
54
|
-
name: `${app.appName} (${app.slug})`,
|
|
55
|
-
value: app
|
|
56
|
-
}));
|
|
57
|
-
|
|
58
|
-
choices.push({
|
|
59
|
-
name: 'Cancel',
|
|
60
|
-
value: null
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// Select app to delete
|
|
64
|
-
const selectedApp = await select({
|
|
65
|
-
message: 'Which Iris app do you want to delete?',
|
|
66
|
-
choices
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
if (!selectedApp) {
|
|
70
|
-
console.log(chalk.gray('Cancelled.'));
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const { slug, appName } = selectedApp;
|
|
75
|
-
|
|
76
|
-
// Show destructive warning
|
|
77
|
-
console.log();
|
|
78
|
-
console.log(chalk.bgRed.bold.white(' ⚠ DESTRUCTIVE OPERATION '));
|
|
79
|
-
console.log(chalk.red('─'.repeat(48)));
|
|
80
|
-
console.log(chalk.white('This will permanently delete:'));
|
|
81
|
-
console.log(chalk.red(` • App from server: ${chalk.cyan(appName)} (${slug})`));
|
|
82
|
-
console.log(chalk.red(` • Local files: ${chalk.gray(`src/iris-apps/${slug}/`)}`));
|
|
83
|
-
console.log(chalk.red(` • Navigation menu entry on Magentrix`));
|
|
84
|
-
console.log();
|
|
85
|
-
console.log(chalk.yellow('A recovery backup will be created before deletion.'));
|
|
86
|
-
console.log(chalk.gray('You can restore using: ') + chalk.cyan('magentrix iris-app-recover'));
|
|
87
|
-
console.log(chalk.red('─'.repeat(48)));
|
|
88
|
-
console.log();
|
|
89
|
-
|
|
90
|
-
// Confirm deletion by typing app name
|
|
91
|
-
const confirmation = await input({
|
|
92
|
-
message: `Type the app slug "${slug}" to confirm deletion:`,
|
|
93
|
-
validate: (value) => {
|
|
94
|
-
if (value === slug) return true;
|
|
95
|
-
return `Please type exactly: ${slug}`;
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
if (confirmation !== slug) {
|
|
100
|
-
console.log(chalk.gray('Cancelled.'));
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Acquire delete lock to prevent concurrent deletions
|
|
105
|
-
const lockBasePath = path.join(process.env.HOME || process.env.USERPROFILE || '/tmp', '.magentrix-locks');
|
|
106
|
-
const lockResult = acquireLock(LockTypes.DELETE, {
|
|
107
|
-
context: slug,
|
|
108
|
-
operation: `deleting ${slug}`,
|
|
109
|
-
basePath: lockBasePath
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Track if lock was acquired (permission errors are non-fatal for delete)
|
|
113
|
-
let lockAcquired = lockResult.acquired;
|
|
114
|
-
if (!lockResult.acquired) {
|
|
115
|
-
if (lockResult.error?.includes('permission') || lockResult.error?.includes('EACCES')) {
|
|
116
|
-
console.log(chalk.yellow('Warning: Could not create delete lock (permission issue). Proceeding without lock.'));
|
|
117
|
-
lockAcquired = false;
|
|
118
|
-
} else {
|
|
119
|
-
console.log(chalk.red('Cannot delete app:'));
|
|
120
|
-
console.log(chalk.yellow(lockResult.error));
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
await performDelete(slug, appName);
|
|
127
|
-
} finally {
|
|
128
|
-
if (lockAcquired) {
|
|
129
|
-
releaseLock(LockTypes.DELETE, { context: slug, basePath: lockBasePath });
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Perform the actual delete operation.
|
|
136
|
-
* @param {string} slug - App slug
|
|
137
|
-
* @param {string} appName - App display name
|
|
138
|
-
*/
|
|
139
|
-
async function performDelete(slug, appName) {
|
|
140
|
-
// Check if app is linked
|
|
141
|
-
const linkedProjects = getLinkedProjects();
|
|
142
|
-
const linkedProject = linkedProjects.find(p => p.slug === slug);
|
|
143
|
-
|
|
144
|
-
let shouldUnlink = false;
|
|
145
|
-
if (linkedProject) {
|
|
146
|
-
console.log();
|
|
147
|
-
shouldUnlink = await confirm({
|
|
148
|
-
message: `This app is linked to a Vue project at ${linkedProject.path}. Unlink it?`,
|
|
149
|
-
default: false
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Create backup
|
|
154
|
-
console.log();
|
|
155
|
-
console.log(chalk.blue('Creating recovery backup...'));
|
|
156
|
-
|
|
157
|
-
const appPath = path.join(EXPORT_ROOT, IRIS_APPS_DIR, slug);
|
|
158
|
-
const backupResult = await backupIrisApp(appPath, {
|
|
159
|
-
slug,
|
|
160
|
-
appName,
|
|
161
|
-
linkedProject: linkedProject || null
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
if (!backupResult.success) {
|
|
165
|
-
console.log(chalk.red(`Failed to create backup: ${backupResult.error}`));
|
|
166
|
-
console.log(chalk.yellow('Deletion cancelled for safety.'));
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
console.log(chalk.green(`✓ Backup created: ${backupResult.backupPath}`));
|
|
171
|
-
|
|
172
|
-
// Delete from server
|
|
173
|
-
console.log();
|
|
174
|
-
console.log(chalk.blue('Deleting from Magentrix server...'));
|
|
175
|
-
|
|
176
|
-
const { instanceUrl, token } = await ensureValidCredentials();
|
|
177
|
-
const deleteResult = await deleteIrisAppFromServer(instanceUrl, token.value, slug, {
|
|
178
|
-
updateCache: true // Automatically updates base.json
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
if (!deleteResult.success && deleteResult.error !== 'App not found on server (already deleted)') {
|
|
182
|
-
console.log(chalk.red(`Failed to delete from server: ${deleteResult.error}`));
|
|
183
|
-
console.log(chalk.yellow('Backup preserved. Use ') + chalk.cyan('magentrix iris-app-recover') + chalk.yellow(' to restore.'));
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (deleteResult.error === 'App not found on server (already deleted)') {
|
|
188
|
-
console.log(chalk.yellow('⚠ App not found on server (already deleted)'));
|
|
189
|
-
} else {
|
|
190
|
-
console.log(chalk.green('✓ Deleted from server'));
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (deleteResult.cleanedFromCache) {
|
|
194
|
-
console.log(chalk.green('✓ Cache updated'));
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Delete local files
|
|
198
|
-
console.log(chalk.blue('Deleting local files...'));
|
|
199
|
-
|
|
200
|
-
const localDeleteResult = deleteLocalIrisAppFiles(appPath);
|
|
201
|
-
|
|
202
|
-
if (localDeleteResult.success) {
|
|
203
|
-
if (localDeleteResult.existed) {
|
|
204
|
-
console.log(chalk.green('✓ Local files deleted'));
|
|
205
|
-
} else {
|
|
206
|
-
console.log(chalk.gray(' (No local files found)'));
|
|
207
|
-
}
|
|
208
|
-
} else if (localDeleteResult.isPermissionError) {
|
|
209
|
-
showPermissionError({
|
|
210
|
-
operation: 'delete',
|
|
211
|
-
targetPath: appPath
|
|
212
|
-
});
|
|
213
|
-
console.log();
|
|
214
|
-
console.log(chalk.gray('Note: The app was deleted from the server and cache.'));
|
|
215
|
-
console.log(chalk.gray('Only local file cleanup failed.'));
|
|
216
|
-
console.log();
|
|
217
|
-
} else if (localDeleteResult.isFileLocked) {
|
|
218
|
-
console.log(chalk.yellow('⚠ Could not delete local files - files are in use'));
|
|
219
|
-
console.log(chalk.gray(localDeleteResult.error));
|
|
220
|
-
console.log();
|
|
221
|
-
console.log(chalk.gray('Note: The app was deleted from the server and cache.'));
|
|
222
|
-
console.log(chalk.gray('Close any programs using these files and delete manually.'));
|
|
223
|
-
console.log();
|
|
224
|
-
} else {
|
|
225
|
-
console.log(chalk.yellow(`⚠ Failed to delete local files: ${localDeleteResult.error}`));
|
|
226
|
-
console.log(chalk.gray(`Path: ${appPath}`));
|
|
227
|
-
console.log(chalk.white('You may need to delete manually.'));
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Unlink Vue project if requested
|
|
231
|
-
if (shouldUnlink && linkedProject) {
|
|
232
|
-
console.log(chalk.blue('Unlinking Vue project...'));
|
|
233
|
-
unlinkVueProject(linkedProject.path);
|
|
234
|
-
console.log(chalk.green('✓ Vue project unlinked'));
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Summary
|
|
238
|
-
console.log();
|
|
239
|
-
console.log(chalk.green('─'.repeat(48)));
|
|
240
|
-
if (localDeleteResult.success) {
|
|
241
|
-
console.log(chalk.green.bold('✓ Iris App Deleted Successfully!'));
|
|
242
|
-
} else {
|
|
243
|
-
console.log(chalk.yellow.bold('⚠ Iris App Partially Deleted'));
|
|
244
|
-
console.log();
|
|
245
|
-
console.log(chalk.green('✓ Deleted from server'));
|
|
246
|
-
console.log(chalk.green('✓ Cache updated'));
|
|
247
|
-
console.log(chalk.yellow('⚠ Local files require manual deletion'));
|
|
248
|
-
}
|
|
249
|
-
console.log();
|
|
250
|
-
console.log(chalk.cyan('Recovery:'));
|
|
251
|
-
console.log(chalk.white(` Backup saved to: ${chalk.gray(backupResult.backupPath)}`));
|
|
252
|
-
console.log(chalk.white(` To restore, run: ${chalk.cyan('magentrix iris-app-recover')}`));
|
|
253
|
-
console.log();
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
export default irisDelete;
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { select, input, confirm } from '@inquirer/prompts';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import Config from '../../utils/config.js';
|
|
5
|
+
import { ensureValidCredentials } from '../../utils/cli/helpers/ensureCredentials.js';
|
|
6
|
+
import { backupIrisApp } from '../../utils/iris/backup.js';
|
|
7
|
+
import { getLinkedProjects, unlinkVueProject } from '../../utils/iris/linker.js';
|
|
8
|
+
import { deleteIrisAppFromServer, deleteLocalIrisAppFiles } from '../../utils/iris/deleteHelper.js';
|
|
9
|
+
import { showPermissionError } from '../../utils/permissionError.js';
|
|
10
|
+
import { EXPORT_ROOT, IRIS_APPS_DIR } from '../../vars/global.js';
|
|
11
|
+
import { acquireLock, releaseLock, LockTypes } from '../../utils/iris/lock.js';
|
|
12
|
+
import { formatFileLockError } from '../../utils/iris/errors.js';
|
|
13
|
+
|
|
14
|
+
const config = new Config();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* iris-app-delete command - Delete a published Iris app with backup and recovery.
|
|
18
|
+
*/
|
|
19
|
+
export const irisDelete = async () => {
|
|
20
|
+
process.stdout.write('\x1Bc'); // Clear console
|
|
21
|
+
|
|
22
|
+
console.log(chalk.red.bold('\n⚠ Delete Iris App'));
|
|
23
|
+
console.log(chalk.gray('─'.repeat(48)));
|
|
24
|
+
console.log();
|
|
25
|
+
|
|
26
|
+
// Get list of published apps from base.json
|
|
27
|
+
const cachedResults = config.read(null, { filename: "base.json" });
|
|
28
|
+
const cachedIrisApps = Object.values(cachedResults || {})
|
|
29
|
+
.filter(entry => entry.type === 'IrisApp' || entry.Type === 'IrisApp')
|
|
30
|
+
.map(entry => {
|
|
31
|
+
const slug = entry.folderName || (entry.recordId && entry.recordId.startsWith('iris-app:')
|
|
32
|
+
? entry.recordId.replace('iris-app:', '')
|
|
33
|
+
: null);
|
|
34
|
+
return {
|
|
35
|
+
slug,
|
|
36
|
+
appName: entry.appName || slug,
|
|
37
|
+
folderName: entry.folderName || slug
|
|
38
|
+
};
|
|
39
|
+
})
|
|
40
|
+
.filter(app => app.slug);
|
|
41
|
+
|
|
42
|
+
if (cachedIrisApps.length === 0) {
|
|
43
|
+
console.log(chalk.yellow('No published Iris apps found.'));
|
|
44
|
+
console.log();
|
|
45
|
+
console.log(chalk.gray('Published apps appear here after running:'));
|
|
46
|
+
console.log(chalk.white(` 1. ${chalk.cyan('magentrix vue-run-build')}`));
|
|
47
|
+
console.log(chalk.white(` 2. ${chalk.cyan('magentrix publish')}`));
|
|
48
|
+
console.log();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Build choices
|
|
53
|
+
const choices = cachedIrisApps.map(app => ({
|
|
54
|
+
name: `${app.appName} (${app.slug})`,
|
|
55
|
+
value: app
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
choices.push({
|
|
59
|
+
name: 'Cancel',
|
|
60
|
+
value: null
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Select app to delete
|
|
64
|
+
const selectedApp = await select({
|
|
65
|
+
message: 'Which Iris app do you want to delete?',
|
|
66
|
+
choices
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!selectedApp) {
|
|
70
|
+
console.log(chalk.gray('Cancelled.'));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const { slug, appName } = selectedApp;
|
|
75
|
+
|
|
76
|
+
// Show destructive warning
|
|
77
|
+
console.log();
|
|
78
|
+
console.log(chalk.bgRed.bold.white(' ⚠ DESTRUCTIVE OPERATION '));
|
|
79
|
+
console.log(chalk.red('─'.repeat(48)));
|
|
80
|
+
console.log(chalk.white('This will permanently delete:'));
|
|
81
|
+
console.log(chalk.red(` • App from server: ${chalk.cyan(appName)} (${slug})`));
|
|
82
|
+
console.log(chalk.red(` • Local files: ${chalk.gray(`src/iris-apps/${slug}/`)}`));
|
|
83
|
+
console.log(chalk.red(` • Navigation menu entry on Magentrix`));
|
|
84
|
+
console.log();
|
|
85
|
+
console.log(chalk.yellow('A recovery backup will be created before deletion.'));
|
|
86
|
+
console.log(chalk.gray('You can restore using: ') + chalk.cyan('magentrix iris-app-recover'));
|
|
87
|
+
console.log(chalk.red('─'.repeat(48)));
|
|
88
|
+
console.log();
|
|
89
|
+
|
|
90
|
+
// Confirm deletion by typing app name
|
|
91
|
+
const confirmation = await input({
|
|
92
|
+
message: `Type the app slug "${slug}" to confirm deletion:`,
|
|
93
|
+
validate: (value) => {
|
|
94
|
+
if (value === slug) return true;
|
|
95
|
+
return `Please type exactly: ${slug}`;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (confirmation !== slug) {
|
|
100
|
+
console.log(chalk.gray('Cancelled.'));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Acquire delete lock to prevent concurrent deletions
|
|
105
|
+
const lockBasePath = path.join(process.env.HOME || process.env.USERPROFILE || '/tmp', '.magentrix-locks');
|
|
106
|
+
const lockResult = acquireLock(LockTypes.DELETE, {
|
|
107
|
+
context: slug,
|
|
108
|
+
operation: `deleting ${slug}`,
|
|
109
|
+
basePath: lockBasePath
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Track if lock was acquired (permission errors are non-fatal for delete)
|
|
113
|
+
let lockAcquired = lockResult.acquired;
|
|
114
|
+
if (!lockResult.acquired) {
|
|
115
|
+
if (lockResult.error?.includes('permission') || lockResult.error?.includes('EACCES')) {
|
|
116
|
+
console.log(chalk.yellow('Warning: Could not create delete lock (permission issue). Proceeding without lock.'));
|
|
117
|
+
lockAcquired = false;
|
|
118
|
+
} else {
|
|
119
|
+
console.log(chalk.red('Cannot delete app:'));
|
|
120
|
+
console.log(chalk.yellow(lockResult.error));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
await performDelete(slug, appName);
|
|
127
|
+
} finally {
|
|
128
|
+
if (lockAcquired) {
|
|
129
|
+
releaseLock(LockTypes.DELETE, { context: slug, basePath: lockBasePath });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Perform the actual delete operation.
|
|
136
|
+
* @param {string} slug - App slug
|
|
137
|
+
* @param {string} appName - App display name
|
|
138
|
+
*/
|
|
139
|
+
async function performDelete(slug, appName) {
|
|
140
|
+
// Check if app is linked
|
|
141
|
+
const linkedProjects = getLinkedProjects();
|
|
142
|
+
const linkedProject = linkedProjects.find(p => p.slug === slug);
|
|
143
|
+
|
|
144
|
+
let shouldUnlink = false;
|
|
145
|
+
if (linkedProject) {
|
|
146
|
+
console.log();
|
|
147
|
+
shouldUnlink = await confirm({
|
|
148
|
+
message: `This app is linked to a Vue project at ${linkedProject.path}. Unlink it?`,
|
|
149
|
+
default: false
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Create backup
|
|
154
|
+
console.log();
|
|
155
|
+
console.log(chalk.blue('Creating recovery backup...'));
|
|
156
|
+
|
|
157
|
+
const appPath = path.join(EXPORT_ROOT, IRIS_APPS_DIR, slug);
|
|
158
|
+
const backupResult = await backupIrisApp(appPath, {
|
|
159
|
+
slug,
|
|
160
|
+
appName,
|
|
161
|
+
linkedProject: linkedProject || null
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
if (!backupResult.success) {
|
|
165
|
+
console.log(chalk.red(`Failed to create backup: ${backupResult.error}`));
|
|
166
|
+
console.log(chalk.yellow('Deletion cancelled for safety.'));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
console.log(chalk.green(`✓ Backup created: ${backupResult.backupPath}`));
|
|
171
|
+
|
|
172
|
+
// Delete from server
|
|
173
|
+
console.log();
|
|
174
|
+
console.log(chalk.blue('Deleting from Magentrix server...'));
|
|
175
|
+
|
|
176
|
+
const { instanceUrl, token } = await ensureValidCredentials();
|
|
177
|
+
const deleteResult = await deleteIrisAppFromServer(instanceUrl, token.value, slug, {
|
|
178
|
+
updateCache: true // Automatically updates base.json
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
if (!deleteResult.success && deleteResult.error !== 'App not found on server (already deleted)') {
|
|
182
|
+
console.log(chalk.red(`Failed to delete from server: ${deleteResult.error}`));
|
|
183
|
+
console.log(chalk.yellow('Backup preserved. Use ') + chalk.cyan('magentrix iris-app-recover') + chalk.yellow(' to restore.'));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (deleteResult.error === 'App not found on server (already deleted)') {
|
|
188
|
+
console.log(chalk.yellow('⚠ App not found on server (already deleted)'));
|
|
189
|
+
} else {
|
|
190
|
+
console.log(chalk.green('✓ Deleted from server'));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (deleteResult.cleanedFromCache) {
|
|
194
|
+
console.log(chalk.green('✓ Cache updated'));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Delete local files
|
|
198
|
+
console.log(chalk.blue('Deleting local files...'));
|
|
199
|
+
|
|
200
|
+
const localDeleteResult = deleteLocalIrisAppFiles(appPath);
|
|
201
|
+
|
|
202
|
+
if (localDeleteResult.success) {
|
|
203
|
+
if (localDeleteResult.existed) {
|
|
204
|
+
console.log(chalk.green('✓ Local files deleted'));
|
|
205
|
+
} else {
|
|
206
|
+
console.log(chalk.gray(' (No local files found)'));
|
|
207
|
+
}
|
|
208
|
+
} else if (localDeleteResult.isPermissionError) {
|
|
209
|
+
showPermissionError({
|
|
210
|
+
operation: 'delete',
|
|
211
|
+
targetPath: appPath
|
|
212
|
+
});
|
|
213
|
+
console.log();
|
|
214
|
+
console.log(chalk.gray('Note: The app was deleted from the server and cache.'));
|
|
215
|
+
console.log(chalk.gray('Only local file cleanup failed.'));
|
|
216
|
+
console.log();
|
|
217
|
+
} else if (localDeleteResult.isFileLocked) {
|
|
218
|
+
console.log(chalk.yellow('⚠ Could not delete local files - files are in use'));
|
|
219
|
+
console.log(chalk.gray(localDeleteResult.error));
|
|
220
|
+
console.log();
|
|
221
|
+
console.log(chalk.gray('Note: The app was deleted from the server and cache.'));
|
|
222
|
+
console.log(chalk.gray('Close any programs using these files and delete manually.'));
|
|
223
|
+
console.log();
|
|
224
|
+
} else {
|
|
225
|
+
console.log(chalk.yellow(`⚠ Failed to delete local files: ${localDeleteResult.error}`));
|
|
226
|
+
console.log(chalk.gray(`Path: ${appPath}`));
|
|
227
|
+
console.log(chalk.white('You may need to delete manually.'));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Unlink Vue project if requested
|
|
231
|
+
if (shouldUnlink && linkedProject) {
|
|
232
|
+
console.log(chalk.blue('Unlinking Vue project...'));
|
|
233
|
+
unlinkVueProject(linkedProject.path);
|
|
234
|
+
console.log(chalk.green('✓ Vue project unlinked'));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Summary
|
|
238
|
+
console.log();
|
|
239
|
+
console.log(chalk.green('─'.repeat(48)));
|
|
240
|
+
if (localDeleteResult.success) {
|
|
241
|
+
console.log(chalk.green.bold('✓ Iris App Deleted Successfully!'));
|
|
242
|
+
} else {
|
|
243
|
+
console.log(chalk.yellow.bold('⚠ Iris App Partially Deleted'));
|
|
244
|
+
console.log();
|
|
245
|
+
console.log(chalk.green('✓ Deleted from server'));
|
|
246
|
+
console.log(chalk.green('✓ Cache updated'));
|
|
247
|
+
console.log(chalk.yellow('⚠ Local files require manual deletion'));
|
|
248
|
+
}
|
|
249
|
+
console.log();
|
|
250
|
+
console.log(chalk.cyan('Recovery:'));
|
|
251
|
+
console.log(chalk.white(` Backup saved to: ${chalk.gray(backupResult.backupPath)}`));
|
|
252
|
+
console.log(chalk.white(` To restore, run: ${chalk.cyan('magentrix iris-app-recover')}`));
|
|
253
|
+
console.log();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export default irisDelete;
|