i18ntk 2.2.0 → 2.3.1
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/README.md +83 -50
- package/main/i18ntk-backup-class.js +37 -41
- package/main/i18ntk-backup.js +28 -30
- package/main/i18ntk-doctor.js +7 -6
- package/main/i18ntk-init.js +44 -8
- package/main/i18ntk-sizing.js +7 -8
- package/main/i18ntk-usage.js +17 -5
- package/main/i18ntk-validate.js +72 -22
- package/main/manage/commands/AnalyzeCommand.js +12 -14
- package/main/manage/commands/CommandRouter.js +15 -12
- package/main/manage/commands/FixerCommand.js +92 -36
- package/main/manage/commands/ValidateCommand.js +78 -27
- package/main/manage/index.js +158 -148
- package/main/manage/managers/DebugMenu.js +6 -6
- package/main/manage/managers/InteractiveMenu.js +6 -6
- package/main/manage/managers/LanguageMenu.js +5 -4
- package/main/manage/managers/SettingsMenu.js +6 -6
- package/main/manage/services/AuthenticationService.js +5 -6
- package/main/manage/services/ConfigurationService.js +22 -34
- package/main/manage/services/FileManagementService.js +6 -6
- package/main/manage/services/InitService.js +44 -8
- package/main/manage/services/UsageService.js +17 -5
- package/package.json +8 -7
- package/settings/settings-cli.js +2 -2
- package/settings/settings-manager.js +984 -968
- package/utils/config-helper.js +27 -16
- package/utils/config-manager.js +8 -7
- package/utils/i18n-helper.js +9 -13
- package/utils/init-helper.js +3 -2
- package/utils/json-output.js +11 -10
- package/utils/logger.js +4 -4
- package/utils/safe-json.js +3 -3
- package/utils/secure-backup.js +8 -7
- package/utils/setup-enforcer.js +63 -98
package/main/manage/index.js
CHANGED
|
@@ -13,16 +13,21 @@
|
|
|
13
13
|
* npm run i18ntk:manage -- --help
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
const path = require('path');
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const {
|
|
22
|
-
const {
|
|
23
|
-
const
|
|
24
|
-
const {
|
|
25
|
-
const
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const AdminAuth = require('../../utils/admin-auth');
|
|
19
|
+
const SecurityUtils = require('../../utils/security');
|
|
20
|
+
const configManager = require('../../settings/settings-manager');
|
|
21
|
+
const { validateSourceDir } = require('../../utils/config-helper');
|
|
22
|
+
const { checkInitialized } = require('../../utils/init-helper');
|
|
23
|
+
const { showFrameworkWarningOnce } = require('../../utils/cli-helper');
|
|
24
|
+
const { createPrompt, isInteractive } = require('../../utils/prompt-helper');
|
|
25
|
+
const { loadTranslations, t, refreshLanguageFromSettings} = require('../../utils/i18n-helper');
|
|
26
|
+
const cliHelper = require('../../utils/cli-helper');
|
|
27
|
+
const { blue } = require('../../utils/colors-new');
|
|
28
|
+
const { loadConfig, saveConfig, ensureConfigDefaults } = require('../../utils/config');
|
|
29
|
+
const SettingsCLI = require('../../settings/settings-cli');
|
|
30
|
+
const pkg = require('../../package.json');
|
|
26
31
|
const SetupEnforcer = require('../../utils/setup-enforcer');
|
|
27
32
|
const CommandRouter = require('./commands/CommandRouter');
|
|
28
33
|
|
|
@@ -73,9 +78,8 @@ class I18nManager {
|
|
|
73
78
|
loadTranslations(uiLanguage);
|
|
74
79
|
|
|
75
80
|
// Validate source directory exists
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
validateSourceDir(this.config.sourceDir, 'i18ntk-manage');
|
|
81
|
+
try {
|
|
82
|
+
validateSourceDir(this.config.sourceDir, 'i18ntk-manage');
|
|
79
83
|
} catch (err) {
|
|
80
84
|
console.log(t('init.requiredTitle'));
|
|
81
85
|
console.log(t('init.requiredBody'));
|
|
@@ -132,10 +136,10 @@ class I18nManager {
|
|
|
132
136
|
if (SecurityUtils.safeExistsSync(resolvedPath)) {
|
|
133
137
|
// Check if it contains language directories
|
|
134
138
|
try {
|
|
135
|
-
const items =
|
|
139
|
+
const items = fs.readdirSync(resolvedPath);
|
|
136
140
|
const hasLanguageDirs = items.some(item => {
|
|
137
141
|
const itemPath = path.join(resolvedPath, item);
|
|
138
|
-
return
|
|
142
|
+
return fs.statSync(itemPath).isDirectory() &&
|
|
139
143
|
['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'].includes(item);
|
|
140
144
|
});
|
|
141
145
|
|
|
@@ -170,7 +174,6 @@ class I18nManager {
|
|
|
170
174
|
};
|
|
171
175
|
|
|
172
176
|
try {
|
|
173
|
-
const { checkInitialized } = require('../../utils/init-helper');
|
|
174
177
|
const initStatus = await checkInitialized();
|
|
175
178
|
if (initStatus?.initialized || initStatus?.config?.setup?.completed) {
|
|
176
179
|
markInternalFrameworkDetected();
|
|
@@ -335,8 +338,8 @@ class I18nManager {
|
|
|
335
338
|
}
|
|
336
339
|
clearStartupTimeout();
|
|
337
340
|
|
|
338
|
-
prompt = createPrompt({ noPrompt: args.noPrompt
|
|
339
|
-
const interactive = isInteractive({ noPrompt: args.noPrompt
|
|
341
|
+
prompt = createPrompt({ noPrompt: args.noPrompt });
|
|
342
|
+
const interactive = isInteractive({ noPrompt: args.noPrompt });
|
|
340
343
|
|
|
341
344
|
// Load settings and UI language
|
|
342
345
|
const settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
|
|
@@ -346,15 +349,10 @@ class I18nManager {
|
|
|
346
349
|
// Initialize CommandRouter
|
|
347
350
|
this.commandRouter = new CommandRouter(this.config, this.ui, this.adminAuth);
|
|
348
351
|
|
|
349
|
-
if (args.
|
|
350
|
-
this.
|
|
351
|
-
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
if (args.help) {
|
|
355
|
-
this.showHelp();
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
352
|
+
if (args.help) {
|
|
353
|
+
this.showHelp();
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
358
356
|
|
|
359
357
|
let cfgAfterInitCheck = {};
|
|
360
358
|
if (interactive && !shouldSkipInitCheck) {
|
|
@@ -366,7 +364,7 @@ class I18nManager {
|
|
|
366
364
|
}
|
|
367
365
|
|
|
368
366
|
this.config = { ...this.config, ...cfgAfterInitCheck };
|
|
369
|
-
await this.initialize({ interactive, noPrompt: args.noPrompt
|
|
367
|
+
await this.initialize({ interactive, noPrompt: args.noPrompt });
|
|
370
368
|
clearStartupTimeout();
|
|
371
369
|
let commandToExecute = requestedCommand;
|
|
372
370
|
|
|
@@ -380,8 +378,7 @@ class I18nManager {
|
|
|
380
378
|
// Handle debug flag
|
|
381
379
|
if (args.debug) {
|
|
382
380
|
// Enable debug mode for this session
|
|
383
|
-
|
|
384
|
-
console.log(blue('Debug mode enabled'));
|
|
381
|
+
console.log(blue('Debug mode enabled'));
|
|
385
382
|
}
|
|
386
383
|
|
|
387
384
|
if (commandToExecute) {
|
|
@@ -489,11 +486,7 @@ class I18nManager {
|
|
|
489
486
|
// ... existing code for ensureInitializedOrExit, maybePromptFramework, showInteractiveMenu, etc. ...
|
|
490
487
|
|
|
491
488
|
async ensureInitializedOrExit(prompt) {
|
|
492
|
-
const {
|
|
493
|
-
const cliHelper = require('../../utils/cli-helper');
|
|
494
|
-
const pkg = require('../../package.json');
|
|
495
|
-
|
|
496
|
-
const { initialized, config } = await checkInitialized();
|
|
489
|
+
const { initialized, config } = await checkInitialized();
|
|
497
490
|
|
|
498
491
|
if (!initialized) {
|
|
499
492
|
console.log('\nThis project is not yet initialized with i18ntk.');
|
|
@@ -608,10 +601,7 @@ class I18nManager {
|
|
|
608
601
|
if (!SecurityUtils) {
|
|
609
602
|
throw new Error('SecurityUtils is not available. This may indicate a module loading issue.');
|
|
610
603
|
}
|
|
611
|
-
const
|
|
612
|
-
const path = require('path');
|
|
613
|
-
|
|
614
|
-
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
604
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
615
605
|
const pyprojectPath = path.join(process.cwd(), 'pyproject.toml');
|
|
616
606
|
const requirementsPath = path.join(process.cwd(), 'requirements.txt');
|
|
617
607
|
const goModPath = path.join(process.cwd(), 'go.mod');
|
|
@@ -622,7 +612,6 @@ class I18nManager {
|
|
|
622
612
|
let detectedFramework = 'generic';
|
|
623
613
|
|
|
624
614
|
try {
|
|
625
|
-
const { checkInitialized } = require('../../utils/init-helper');
|
|
626
615
|
const initStatus = await checkInitialized();
|
|
627
616
|
if (initStatus?.initialized || initStatus?.config?.setup?.completed) {
|
|
628
617
|
return { detectedLanguage: 'javascript', detectedFramework: 'i18ntk' };
|
|
@@ -738,9 +727,7 @@ class I18nManager {
|
|
|
738
727
|
}
|
|
739
728
|
|
|
740
729
|
async customGlob(patterns, options = {}) {
|
|
741
|
-
const
|
|
742
|
-
const path = require('path');
|
|
743
|
-
const cwd = options.cwd || process.cwd();
|
|
730
|
+
const cwd = options.cwd || process.cwd();
|
|
744
731
|
const ignorePatterns = options.ignore || [];
|
|
745
732
|
|
|
746
733
|
function matchesPattern(filename, pattern) {
|
|
@@ -944,8 +931,7 @@ class I18nManager {
|
|
|
944
931
|
const authRequired = await this.adminAuth.isAuthRequiredForScript('summaryReports');
|
|
945
932
|
if (authRequired) {
|
|
946
933
|
console.log(`\n${t('adminCli.protectedAccess')}`);
|
|
947
|
-
const
|
|
948
|
-
const pin = await cliHelper.promptPin(t('adminCli.enterPin') + ': ');
|
|
934
|
+
const pin = await cliHelper.promptPin(t('adminCli.enterPin') + ': ');
|
|
949
935
|
const isValid = await this.adminAuth.verifyPin(pin);
|
|
950
936
|
|
|
951
937
|
if (!isValid) {
|
|
@@ -1061,8 +1047,7 @@ class I18nManager {
|
|
|
1061
1047
|
console.log(t('language.changed', { language: selectedLang.name }));
|
|
1062
1048
|
|
|
1063
1049
|
// Force reload translations for the entire system
|
|
1064
|
-
|
|
1065
|
-
loadTranslations(selectedLang.code);
|
|
1050
|
+
loadTranslations(selectedLang.code);
|
|
1066
1051
|
|
|
1067
1052
|
// Return to main menu with new language
|
|
1068
1053
|
await this.prompt('\n' + t('language.pressEnterToContinue'));
|
|
@@ -1079,8 +1064,7 @@ class I18nManager {
|
|
|
1079
1064
|
const authRequired = await this.adminAuth.isAuthRequiredForScript('debugMenu');
|
|
1080
1065
|
if (authRequired) {
|
|
1081
1066
|
console.log(`\n${t('adminPin.protectedAccess')}`);
|
|
1082
|
-
const
|
|
1083
|
-
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
1067
|
+
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
1084
1068
|
const isValid = await this.adminAuth.verifyPin(pin);
|
|
1085
1069
|
|
|
1086
1070
|
if (!isValid) {
|
|
@@ -1140,35 +1124,46 @@ class I18nManager {
|
|
|
1140
1124
|
console.log(`\n${t('debug.recentDebugLogs')}`);
|
|
1141
1125
|
console.log('============================================================');
|
|
1142
1126
|
|
|
1143
|
-
try {
|
|
1144
|
-
const logsDir = path.join(__dirname, '..', '..', 'scripts', 'debug', 'logs');
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
.
|
|
1149
|
-
|
|
1150
|
-
const
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
const
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1127
|
+
try {
|
|
1128
|
+
const logsDir = path.join(__dirname, '..', '..', 'scripts', 'debug', 'logs');
|
|
1129
|
+
const validatedLogsDir = SecurityUtils.validatePath(logsDir, process.cwd());
|
|
1130
|
+
if (validatedLogsDir && SecurityUtils.safeExistsSync(validatedLogsDir, process.cwd())) {
|
|
1131
|
+
const files = fs.readdirSync(validatedLogsDir)
|
|
1132
|
+
.filter(file => file.endsWith('.log') || file.endsWith('.txt'))
|
|
1133
|
+
.sort((a, b) => {
|
|
1134
|
+
const statA = fs.statSync(path.join(validatedLogsDir, a));
|
|
1135
|
+
const statB = fs.statSync(path.join(validatedLogsDir, b));
|
|
1136
|
+
return statB.mtime - statA.mtime;
|
|
1137
|
+
})
|
|
1138
|
+
.slice(0, 5);
|
|
1139
|
+
|
|
1140
|
+
if (files.length > 0) {
|
|
1141
|
+
files.forEach((file, index) => {
|
|
1142
|
+
const filePath = path.join(validatedLogsDir, file);
|
|
1143
|
+
const validatedFilePath = SecurityUtils.validatePath(filePath, validatedLogsDir);
|
|
1144
|
+
if (!validatedFilePath) {
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
const stats = fs.statSync(validatedFilePath);
|
|
1148
|
+
console.log(`${index + 1}. ${file} (${stats.mtime.toLocaleString()})`);
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1151
|
+
const choice = await this.prompt('\n' + t('debug.selectLogPrompt', { count: files.length }));
|
|
1152
|
+
const fileIndex = parseInt(choice) - 1;
|
|
1153
|
+
|
|
1154
|
+
if (fileIndex >= 0 && fileIndex < files.length) {
|
|
1155
|
+
const logPath = SecurityUtils.validatePath(path.join(validatedLogsDir, files[fileIndex]), validatedLogsDir);
|
|
1156
|
+
if (!logPath) {
|
|
1157
|
+
console.log(t('debug.debugLogsDirectoryNotFound'));
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
const logContent = SecurityUtils.safeReadFileSync(logPath, validatedLogsDir, 'utf8');
|
|
1161
|
+
console.log(`\n${t('debug.contentOf', { filename: files[fileIndex] })}:`);
|
|
1162
|
+
console.log('============================================================');
|
|
1163
|
+
const tailLines = String(logContent || '').split(/\r?\n/).slice(-40).join('\n');
|
|
1164
|
+
console.log(tailLines);
|
|
1165
|
+
console.log('============================================================');
|
|
1166
|
+
}
|
|
1172
1167
|
} else {
|
|
1173
1168
|
console.log(t('debug.noDebugLogsFound'));
|
|
1174
1169
|
}
|
|
@@ -1188,8 +1183,7 @@ class I18nManager {
|
|
|
1188
1183
|
const authRequired = await this.adminAuth.isAuthRequiredForScript('deleteReports');
|
|
1189
1184
|
if (authRequired) {
|
|
1190
1185
|
console.log(`\n${t('adminPin.protectedAccess')}`);
|
|
1191
|
-
const
|
|
1192
|
-
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
1186
|
+
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
1193
1187
|
const isValid = await this.adminAuth.verifyPin(pin);
|
|
1194
1188
|
|
|
1195
1189
|
if (!isValid) {
|
|
@@ -1215,22 +1209,25 @@ class I18nManager {
|
|
|
1215
1209
|
{ path: path.join(process.cwd(), 'utils', 'i18ntk-reports'), name: 'Utils Reports', type: 'reports' }
|
|
1216
1210
|
].filter(dir => dir.path && typeof dir.path === 'string');
|
|
1217
1211
|
|
|
1218
|
-
try {
|
|
1219
|
-
console.log(t('operations.scanningForFiles'));
|
|
1220
|
-
|
|
1221
|
-
let availableDirs = [];
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1212
|
+
try {
|
|
1213
|
+
console.log(t('operations.scanningForFiles'));
|
|
1214
|
+
|
|
1215
|
+
let availableDirs = [];
|
|
1216
|
+
const projectRoot = process.cwd();
|
|
1217
|
+
|
|
1218
|
+
// Check which directories exist and have files
|
|
1219
|
+
for (const dir of targetDirs) {
|
|
1220
|
+
const validatedDirPath = SecurityUtils.validatePath(dir.path, projectRoot);
|
|
1221
|
+
if (validatedDirPath && SecurityUtils.safeExistsSync(validatedDirPath, projectRoot)) {
|
|
1222
|
+
const files = this.getAllReportFiles(validatedDirPath, validatedDirPath);
|
|
1223
|
+
if (files.length > 0) {
|
|
1224
|
+
availableDirs.push({
|
|
1225
|
+
...dir,
|
|
1226
|
+
path: validatedDirPath,
|
|
1227
|
+
files: files.map(file => ({ path: file, dir: validatedDirPath })),
|
|
1228
|
+
count: files.length
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1234
1231
|
}
|
|
1235
1232
|
}
|
|
1236
1233
|
|
|
@@ -1329,15 +1326,20 @@ class I18nManager {
|
|
|
1329
1326
|
if (confirm.toLowerCase() === 'y' || confirm.toLowerCase() === 'yes') {
|
|
1330
1327
|
let deletedCount = 0;
|
|
1331
1328
|
|
|
1332
|
-
for (const fileInfo of filesToDelete) {
|
|
1333
|
-
try {
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1329
|
+
for (const fileInfo of filesToDelete) {
|
|
1330
|
+
try {
|
|
1331
|
+
const validatedDeletePath = SecurityUtils.validatePath(fileInfo.path, process.cwd());
|
|
1332
|
+
if (!validatedDeletePath) {
|
|
1333
|
+
console.log(t('operations.failedToDeleteFile', { filename: path.basename(fileInfo.path), error: 'Invalid path' }));
|
|
1334
|
+
continue;
|
|
1335
|
+
}
|
|
1336
|
+
fs.unlinkSync(validatedDeletePath);
|
|
1337
|
+
console.log(t('operations.deletedFile', { filename: path.basename(validatedDeletePath) }));
|
|
1338
|
+
deletedCount++;
|
|
1339
|
+
} catch (error) {
|
|
1340
|
+
console.log(t('operations.failedToDeleteFile', { filename: path.basename(fileInfo.path), error: error.message }));
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1341
1343
|
|
|
1342
1344
|
console.log(`\n🎉 Successfully deleted ${deletedCount} files!`);
|
|
1343
1345
|
} else {
|
|
@@ -1352,30 +1354,41 @@ class I18nManager {
|
|
|
1352
1354
|
await this.showInteractiveMenu();
|
|
1353
1355
|
}
|
|
1354
1356
|
|
|
1355
|
-
getAllReportFiles(dir) {
|
|
1356
|
-
if (!dir || typeof dir !== 'string') {
|
|
1357
|
-
return [];
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
let files = [];
|
|
1361
|
-
|
|
1362
|
-
try {
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
if (
|
|
1375
|
-
|
|
1376
|
-
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1357
|
+
getAllReportFiles(dir, rootDir = dir) {
|
|
1358
|
+
if (!dir || typeof dir !== 'string') {
|
|
1359
|
+
return [];
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
let files = [];
|
|
1363
|
+
|
|
1364
|
+
try {
|
|
1365
|
+
const validatedDir = SecurityUtils.validatePath(dir, rootDir);
|
|
1366
|
+
if (!validatedDir || !SecurityUtils.safeExistsSync(validatedDir, rootDir)) {
|
|
1367
|
+
return [];
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
const items = fs.readdirSync(validatedDir);
|
|
1371
|
+
for (const item of items) {
|
|
1372
|
+
const fullPath = path.join(validatedDir, item);
|
|
1373
|
+
|
|
1374
|
+
try {
|
|
1375
|
+
const linkStat = fs.lstatSync(fullPath);
|
|
1376
|
+
if (linkStat.isSymbolicLink()) {
|
|
1377
|
+
continue;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
const safeFullPath = SecurityUtils.validatePath(fullPath, rootDir);
|
|
1381
|
+
if (!safeFullPath) {
|
|
1382
|
+
continue;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
const stat = fs.statSync(safeFullPath);
|
|
1386
|
+
|
|
1387
|
+
if (stat.isDirectory()) {
|
|
1388
|
+
files.push(...this.getAllReportFiles(safeFullPath, rootDir));
|
|
1389
|
+
} else if (
|
|
1390
|
+
// Common report file extensions
|
|
1391
|
+
item.endsWith('.json') ||
|
|
1379
1392
|
item.endsWith('.html') ||
|
|
1380
1393
|
item.endsWith('.txt') ||
|
|
1381
1394
|
item.endsWith('.log') ||
|
|
@@ -1386,13 +1399,13 @@ class I18nManager {
|
|
|
1386
1399
|
item.includes('_report.') ||
|
|
1387
1400
|
item.includes('report-') ||
|
|
1388
1401
|
item.includes('report_') ||
|
|
1389
|
-
item.includes('analysis-') ||
|
|
1390
|
-
item.includes('validation-')
|
|
1391
|
-
) {
|
|
1392
|
-
files.push(
|
|
1393
|
-
}
|
|
1394
|
-
} catch (error) {
|
|
1395
|
-
// Skip individual files that can't be accessed
|
|
1402
|
+
item.includes('analysis-') ||
|
|
1403
|
+
item.includes('validation-')
|
|
1404
|
+
) {
|
|
1405
|
+
files.push(safeFullPath);
|
|
1406
|
+
}
|
|
1407
|
+
} catch (error) {
|
|
1408
|
+
// Skip individual files that can't be accessed
|
|
1396
1409
|
continue;
|
|
1397
1410
|
}
|
|
1398
1411
|
}
|
|
@@ -1404,12 +1417,12 @@ class I18nManager {
|
|
|
1404
1417
|
return files;
|
|
1405
1418
|
}
|
|
1406
1419
|
|
|
1407
|
-
getFilesToDeleteKeepLast(allFiles, keepCount = 3) {
|
|
1408
|
-
// Sort files by modification time (newest first)
|
|
1409
|
-
const sortedFiles = allFiles.sort((a, b) => {
|
|
1410
|
-
try {
|
|
1411
|
-
const statA =
|
|
1412
|
-
const statB =
|
|
1420
|
+
getFilesToDeleteKeepLast(allFiles, keepCount = 3) {
|
|
1421
|
+
// Sort files by modification time (newest first)
|
|
1422
|
+
const sortedFiles = [...allFiles].sort((a, b) => {
|
|
1423
|
+
try {
|
|
1424
|
+
const statA = fs.statSync(a.path || a);
|
|
1425
|
+
const statB = fs.statSync(b.path || b);
|
|
1413
1426
|
return statB.mtime.getTime() - statA.mtime.getTime();
|
|
1414
1427
|
} catch (error) {
|
|
1415
1428
|
// If stat fails, sort by filename as fallback
|
|
@@ -1429,8 +1442,7 @@ class I18nManager {
|
|
|
1429
1442
|
const authRequired = await this.adminAuth.isAuthRequiredForScript('settingsMenu');
|
|
1430
1443
|
if (authRequired) {
|
|
1431
1444
|
console.log(`\n${t('adminPin.protectedAccess')}`);
|
|
1432
|
-
const
|
|
1433
|
-
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
1445
|
+
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
1434
1446
|
const isValid = await this.adminAuth.verifyPin(pin);
|
|
1435
1447
|
|
|
1436
1448
|
if (!isValid) {
|
|
@@ -1443,8 +1455,7 @@ class I18nManager {
|
|
|
1443
1455
|
console.log(t('adminPin.accessGranted'));
|
|
1444
1456
|
}
|
|
1445
1457
|
|
|
1446
|
-
const
|
|
1447
|
-
const settingsCLI = new SettingsCLI();
|
|
1458
|
+
const settingsCLI = new SettingsCLI();
|
|
1448
1459
|
await settingsCLI.run();
|
|
1449
1460
|
} catch (error) {
|
|
1450
1461
|
console.error('❌ Error opening settings:', error.message);
|
|
@@ -1454,8 +1465,7 @@ class I18nManager {
|
|
|
1454
1465
|
}
|
|
1455
1466
|
|
|
1456
1467
|
prompt(question) {
|
|
1457
|
-
|
|
1458
|
-
// If interactive not available, return empty string to avoid hangs
|
|
1468
|
+
// If interactive not available, return empty string to avoid hangs
|
|
1459
1469
|
if (!process.stdin.isTTY || process.stdin.destroyed) {
|
|
1460
1470
|
console.log('\n⚠️ Interactive input not available, using default response.');
|
|
1461
1471
|
return Promise.resolve('');
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* @module managers/DebugMenu
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const { t } = require('../../../utils/i18n-helper');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const { t } = require('../../../utils/i18n-helper');
|
|
9
|
+
const cliHelper = require('../../../utils/cli-helper');
|
|
9
10
|
|
|
10
11
|
module.exports = class DebugMenu {
|
|
11
12
|
constructor(manager) {
|
|
@@ -21,8 +22,7 @@ module.exports = class DebugMenu {
|
|
|
21
22
|
const authRequired = await this.adminAuth.isAuthRequiredForScript('debugMenu');
|
|
22
23
|
if (authRequired) {
|
|
23
24
|
console.log(`\n${t('adminPin.protectedAccess')}`);
|
|
24
|
-
const
|
|
25
|
-
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
25
|
+
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
26
26
|
const isValid = await this.adminAuth.verifyPin(pin);
|
|
27
27
|
|
|
28
28
|
if (!isValid) {
|
|
@@ -137,4 +137,4 @@ module.exports = class DebugMenu {
|
|
|
137
137
|
async show() {
|
|
138
138
|
return this.showDebugMenu();
|
|
139
139
|
}
|
|
140
|
-
};
|
|
140
|
+
};
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* @module managers/InteractiveMenu
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const { t } = require('../../../utils/i18n-helper');
|
|
6
|
+
const { t } = require('../../../utils/i18n-helper');
|
|
7
|
+
const cliHelper = require('../../../utils/cli-helper');
|
|
8
|
+
const summaryTool = require('../../i18ntk-summary');
|
|
7
9
|
|
|
8
10
|
module.exports = class InteractiveMenu {
|
|
9
11
|
constructor(manager) {
|
|
@@ -89,8 +91,7 @@ module.exports = class InteractiveMenu {
|
|
|
89
91
|
const authRequired = await this.adminAuth.isAuthRequiredForScript('summaryReports');
|
|
90
92
|
if (authRequired) {
|
|
91
93
|
console.log(`\n${t('adminCli.protectedAccess')}`);
|
|
92
|
-
const
|
|
93
|
-
const pin = await cliHelper.promptPin(t('adminCli.enterPin') + ': ');
|
|
94
|
+
const pin = await cliHelper.promptPin(t('adminCli.enterPin') + ': ');
|
|
94
95
|
const isValid = await this.adminAuth.verifyPin(pin);
|
|
95
96
|
|
|
96
97
|
if (!isValid) {
|
|
@@ -105,8 +106,7 @@ module.exports = class InteractiveMenu {
|
|
|
105
106
|
|
|
106
107
|
console.log(t('summary.status.generating'));
|
|
107
108
|
try {
|
|
108
|
-
const
|
|
109
|
-
const summary = new summaryTool();
|
|
109
|
+
const summary = new summaryTool();
|
|
110
110
|
await summary.run({ fromMenu: true });
|
|
111
111
|
console.log(t('summary.status.completed'));
|
|
112
112
|
|
|
@@ -174,4 +174,4 @@ module.exports = class InteractiveMenu {
|
|
|
174
174
|
async show() {
|
|
175
175
|
return this.showInteractiveMenu();
|
|
176
176
|
}
|
|
177
|
-
};
|
|
177
|
+
};
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* @module managers/LanguageMenu
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const { t } = require('../../../utils/i18n-helper');
|
|
6
|
+
const { t } = require('../../../utils/i18n-helper');
|
|
7
|
+
const { loadTranslations } = require('../../../utils/i18n-helper');
|
|
8
|
+
const SecurityUtils = require('../../../utils/security');
|
|
7
9
|
|
|
8
10
|
module.exports = class LanguageMenu {
|
|
9
11
|
constructor(manager) {
|
|
@@ -45,8 +47,7 @@ module.exports = class LanguageMenu {
|
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
// Force reload translations for the entire system
|
|
48
|
-
|
|
49
|
-
loadTranslations(selectedLang);
|
|
50
|
+
loadTranslations(selectedLang);
|
|
50
51
|
|
|
51
52
|
// Return to main menu with new language
|
|
52
53
|
await this.manager.prompt('\n' + t('language.pressEnterToContinue'));
|
|
@@ -64,4 +65,4 @@ module.exports = class LanguageMenu {
|
|
|
64
65
|
async show() {
|
|
65
66
|
return this.showLanguageMenu();
|
|
66
67
|
}
|
|
67
|
-
};
|
|
68
|
+
};
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* @module managers/SettingsMenu
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const { t } = require('../../../utils/i18n-helper');
|
|
6
|
+
const { t } = require('../../../utils/i18n-helper');
|
|
7
|
+
const cliHelper = require('../../../utils/cli-helper');
|
|
8
|
+
const SettingsCLI = require('../../../settings/settings-cli');
|
|
7
9
|
|
|
8
10
|
module.exports = class SettingsMenu {
|
|
9
11
|
constructor(manager) {
|
|
@@ -20,8 +22,7 @@ module.exports = class SettingsMenu {
|
|
|
20
22
|
const authRequired = await this.adminAuth.isAuthRequiredForScript('settingsMenu');
|
|
21
23
|
if (authRequired) {
|
|
22
24
|
console.log(`\n${t('adminPin.protectedAccess')}`);
|
|
23
|
-
const
|
|
24
|
-
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
25
|
+
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
25
26
|
const isValid = await this.adminAuth.verifyPin(pin);
|
|
26
27
|
|
|
27
28
|
if (!isValid) {
|
|
@@ -34,8 +35,7 @@ module.exports = class SettingsMenu {
|
|
|
34
35
|
console.log(t('adminPin.accessGranted'));
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
const
|
|
38
|
-
const settingsCLI = new SettingsCLI();
|
|
38
|
+
const settingsCLI = new SettingsCLI();
|
|
39
39
|
await settingsCLI.run();
|
|
40
40
|
} catch (error) {
|
|
41
41
|
console.error('❌ Error opening settings:', error.message);
|
|
@@ -50,4 +50,4 @@ module.exports = class SettingsMenu {
|
|
|
50
50
|
async show() {
|
|
51
51
|
return this.showSettingsMenu();
|
|
52
52
|
}
|
|
53
|
-
};
|
|
53
|
+
};
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* @module services/AuthenticationService
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const AdminAuth = require('../../../utils/admin-auth');
|
|
7
|
+
const AdminAuth = require('../../../utils/admin-auth');
|
|
8
|
+
const cliHelper = require('../../../utils/cli-helper');
|
|
8
9
|
|
|
9
10
|
module.exports = class AuthenticationService {
|
|
10
11
|
constructor(config = {}) {
|
|
@@ -74,8 +75,7 @@ module.exports = class AuthenticationService {
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
console.log(ui ? ui.t('adminCli.authRequired') : 'Admin authentication required');
|
|
77
|
-
const
|
|
78
|
-
const pin = await cliHelper.promptPin(ui ? ui.t('adminCli.enterPin') : 'Enter PIN: ');
|
|
78
|
+
const pin = await cliHelper.promptPin(ui ? ui.t('adminCli.enterPin') : 'Enter PIN: ');
|
|
79
79
|
const isValid = await this.verifyPin(pin);
|
|
80
80
|
|
|
81
81
|
if (!isValid) {
|
|
@@ -131,8 +131,7 @@ module.exports = class AuthenticationService {
|
|
|
131
131
|
* @returns {Promise<string>} User input or empty string
|
|
132
132
|
*/
|
|
133
133
|
async prompt(question) {
|
|
134
|
-
|
|
135
|
-
// If interactive not available, return empty string to avoid hangs
|
|
134
|
+
// If interactive not available, return empty string to avoid hangs
|
|
136
135
|
if (!process.stdin.isTTY || process.stdin.destroyed) {
|
|
137
136
|
console.log('\n⚠️ Interactive input not available, using default response.');
|
|
138
137
|
return Promise.resolve('');
|
|
@@ -260,4 +259,4 @@ module.exports = class AuthenticationService {
|
|
|
260
259
|
process.exit(1);
|
|
261
260
|
}
|
|
262
261
|
}
|
|
263
|
-
};
|
|
262
|
+
};
|