i18ntk 1.10.2 → 2.0.3
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 +1 -1
- package/README.md +141 -1191
- package/main/i18ntk-analyze.js +65 -84
- package/main/i18ntk-backup-class.js +420 -0
- package/main/i18ntk-backup.js +3 -3
- package/main/i18ntk-complete.js +90 -65
- package/main/i18ntk-doctor.js +123 -103
- package/main/i18ntk-fixer.js +61 -725
- package/main/i18ntk-go.js +14 -15
- package/main/i18ntk-init.js +77 -26
- package/main/i18ntk-java.js +27 -32
- package/main/i18ntk-js.js +70 -68
- package/main/i18ntk-manage.js +129 -30
- package/main/i18ntk-php.js +75 -75
- package/main/i18ntk-py.js +55 -56
- package/main/i18ntk-scanner.js +59 -57
- package/main/i18ntk-setup.js +9 -404
- package/main/i18ntk-sizing.js +6 -6
- package/main/i18ntk-summary.js +21 -18
- package/main/i18ntk-ui.js +11 -10
- package/main/i18ntk-usage.js +54 -18
- package/main/i18ntk-validate.js +13 -13
- package/main/manage/commands/AnalyzeCommand.js +1124 -0
- package/main/manage/commands/BackupCommand.js +62 -0
- package/main/manage/commands/CommandRouter.js +295 -0
- package/main/manage/commands/CompleteCommand.js +61 -0
- package/main/manage/commands/DoctorCommand.js +60 -0
- package/main/manage/commands/FixerCommand.js +624 -0
- package/main/manage/commands/InitCommand.js +62 -0
- package/main/manage/commands/ScannerCommand.js +654 -0
- package/main/manage/commands/SizingCommand.js +60 -0
- package/main/manage/commands/SummaryCommand.js +61 -0
- package/main/manage/commands/UsageCommand.js +60 -0
- package/main/manage/commands/ValidateCommand.js +978 -0
- package/main/manage/index-fixed.js +1447 -0
- package/main/manage/index.js +1462 -0
- package/main/manage/managers/DebugMenu.js +140 -0
- package/main/manage/managers/InteractiveMenu.js +177 -0
- package/main/manage/managers/LanguageMenu.js +62 -0
- package/main/manage/managers/SettingsMenu.js +53 -0
- package/main/manage/services/AuthenticationService.js +263 -0
- package/main/manage/services/ConfigurationService-fixed.js +449 -0
- package/main/manage/services/ConfigurationService.js +449 -0
- package/main/manage/services/FileManagementService.js +368 -0
- package/main/manage/services/FrameworkDetectionService.js +458 -0
- package/main/manage/services/InitService.js +1051 -0
- package/main/manage/services/SetupService.js +462 -0
- package/main/manage/services/SummaryService.js +450 -0
- package/main/manage/services/UsageService.js +1502 -0
- package/package.json +32 -29
- package/runtime/enhanced.d.ts +221 -221
- package/runtime/index.d.ts +29 -29
- package/runtime/index.full.d.ts +331 -331
- package/runtime/index.js +7 -6
- package/scripts/build-lite.js +17 -17
- package/scripts/deprecate-versions.js +23 -6
- package/scripts/export-translations.js +5 -5
- package/scripts/fix-all-i18n.js +3 -3
- package/scripts/fix-and-purify-i18n.js +3 -2
- package/scripts/fix-locale-control-chars.js +110 -0
- package/scripts/lint-locales.js +80 -0
- package/scripts/locale-optimizer.js +8 -8
- package/scripts/prepublish.js +21 -21
- package/scripts/security-check.js +117 -117
- package/scripts/sync-translations.js +4 -4
- package/scripts/sync-ui-locales.js +9 -8
- package/scripts/validate-all-translations.js +8 -7
- package/scripts/verify-deprecations.js +157 -161
- package/scripts/verify-translations.js +6 -5
- package/settings/i18ntk-config.json +282 -282
- package/settings/language-config.json +5 -5
- package/settings/settings-cli.js +9 -9
- package/settings/settings-manager.js +18 -18
- package/ui-locales/de.json +2417 -2348
- package/ui-locales/en.json +2415 -2352
- package/ui-locales/es.json +2425 -2353
- package/ui-locales/fr.json +2418 -2348
- package/ui-locales/ja.json +2463 -2361
- package/ui-locales/ru.json +2463 -2359
- package/ui-locales/zh.json +2418 -2351
- package/utils/admin-auth.js +2 -2
- package/utils/admin-cli.js +297 -297
- package/utils/admin-pin.js +9 -9
- package/utils/cli-helper.js +9 -9
- package/utils/config-helper.js +73 -104
- package/utils/config-manager.js +204 -171
- package/utils/config.js +5 -4
- package/utils/env-manager.js +249 -263
- package/utils/framework-detector.js +27 -24
- package/utils/i18n-helper.js +85 -41
- package/utils/init-helper.js +152 -94
- package/utils/json-output.js +98 -98
- package/utils/mini-commander.js +179 -0
- package/utils/missing-key-validator.js +5 -5
- package/utils/plugin-loader.js +40 -29
- package/utils/prompt.js +14 -44
- package/utils/safe-json.js +40 -0
- package/utils/secure-errors.js +3 -3
- package/utils/security-check-improved.js +390 -0
- package/utils/security-config.js +5 -5
- package/utils/security-fixed.js +607 -0
- package/utils/security.js +652 -602
- package/utils/setup-enforcer.js +136 -44
- package/utils/setup-validator.js +33 -32
- package/utils/ultra-performance-optimizer.js +11 -9
- package/utils/watch-locales.js +2 -1
- package/utils/prompt-fixed.js +0 -55
- package/utils/security-check.js +0 -454
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug Menu Manager
|
|
3
|
+
* @module managers/DebugMenu
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const { t } = require('../../../utils/i18n-helper');
|
|
9
|
+
|
|
10
|
+
module.exports = class DebugMenu {
|
|
11
|
+
constructor(manager) {
|
|
12
|
+
this.manager = manager; // Reference to I18nManager for access to methods and properties
|
|
13
|
+
this.adminAuth = manager.adminAuth;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Display the debug tools menu
|
|
18
|
+
*/
|
|
19
|
+
async showDebugMenu() {
|
|
20
|
+
// Check for PIN protection
|
|
21
|
+
const authRequired = await this.adminAuth.isAuthRequiredForScript('debugMenu');
|
|
22
|
+
if (authRequired) {
|
|
23
|
+
console.log(`\n${t('adminPin.protectedAccess')}`);
|
|
24
|
+
const cliHelper = require('../../../utils/cli-helper');
|
|
25
|
+
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
26
|
+
const isValid = await this.adminAuth.verifyPin(pin);
|
|
27
|
+
|
|
28
|
+
if (!isValid) {
|
|
29
|
+
console.log(t('adminPin.invalidPin'));
|
|
30
|
+
await this.manager.prompt(t('menu.pressEnterToContinue'));
|
|
31
|
+
await this.manager.showInteractiveMenu();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log(t('adminPin.accessGranted'));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log(`\n${t('debug.title')}`);
|
|
39
|
+
console.log(t('debug.separator'));
|
|
40
|
+
console.log(t('debug.mainDebuggerSystemDiagnostics'));
|
|
41
|
+
console.log(t('debug.debugLogs'));
|
|
42
|
+
console.log(t('debug.backToMainMenu'));
|
|
43
|
+
|
|
44
|
+
const choice = await this.manager.prompt('\n' + t('debug.selectOption'));
|
|
45
|
+
|
|
46
|
+
switch (choice.trim()) {
|
|
47
|
+
case '1':
|
|
48
|
+
await this.runDebugTool('debugger.js', 'Main Debugger');
|
|
49
|
+
break;
|
|
50
|
+
case '2':
|
|
51
|
+
await this.viewDebugLogs();
|
|
52
|
+
break;
|
|
53
|
+
case '0':
|
|
54
|
+
await this.manager.showInteractiveMenu();
|
|
55
|
+
return;
|
|
56
|
+
default:
|
|
57
|
+
console.log(t('debug.invalidChoiceSelectRange'));
|
|
58
|
+
await this.showDebugMenu();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Run a specific debug tool
|
|
64
|
+
*/
|
|
65
|
+
async runDebugTool(toolName, displayName) {
|
|
66
|
+
console.log(t('debug.runningDebugTool', { displayName }));
|
|
67
|
+
try {
|
|
68
|
+
const toolPath = path.join(__dirname, '..', '..', 'scripts', 'debug', toolName);
|
|
69
|
+
if (fs.existsSync(toolPath)) {
|
|
70
|
+
console.log(`Debug tool available: ${toolName}`);
|
|
71
|
+
console.log(`To run this tool manually: node "${toolPath}"`);
|
|
72
|
+
console.log(`Working directory: ${path.join(__dirname, '..', '..')}`);
|
|
73
|
+
} else {
|
|
74
|
+
console.log(t('debug.debugToolNotFound', { toolName }));
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error(t('debug.errorRunningDebugTool', { displayName, error: error.message }));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
await this.manager.prompt('\n' + t('menu.pressEnterToContinue'));
|
|
81
|
+
await this.showDebugMenu();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* View debug logs
|
|
86
|
+
*/
|
|
87
|
+
async viewDebugLogs() {
|
|
88
|
+
console.log(`\n${t('debug.recentDebugLogs')}`);
|
|
89
|
+
console.log('============================================================');
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const logsDir = path.join(__dirname, '..', '..', 'scripts', 'debug', 'logs');
|
|
93
|
+
if (fs.existsSync(logsDir)) {
|
|
94
|
+
const files = fs.readdirSync(logsDir)
|
|
95
|
+
.filter(file => file.endsWith('.log') || file.endsWith('.txt'))
|
|
96
|
+
.sort((a, b) => {
|
|
97
|
+
const statA = fs.statSync(path.join(logsDir, a));
|
|
98
|
+
const statB = fs.statSync(path.join(logsDir, b));
|
|
99
|
+
return statB.mtime - statA.mtime;
|
|
100
|
+
})
|
|
101
|
+
.slice(0, 5);
|
|
102
|
+
|
|
103
|
+
if (files.length > 0) {
|
|
104
|
+
files.forEach((file, index) => {
|
|
105
|
+
const filePath = path.join(logsDir, file);
|
|
106
|
+
const stats = fs.statSync(filePath);
|
|
107
|
+
console.log(`${index + 1}. ${file} (${stats.mtime.toLocaleString()})`);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const choice = await this.manager.prompt('\n' + t('debug.selectLogPrompt', { count: files.length }));
|
|
111
|
+
const fileIndex = parseInt(choice) - 1;
|
|
112
|
+
|
|
113
|
+
if (fileIndex >= 0 && fileIndex < files.length) {
|
|
114
|
+
const logContent = fs.readFileSync(path.join(logsDir, files[fileIndex]), 'utf8');
|
|
115
|
+
console.log(`\n${t('debug.contentOf', { filename: files[fileIndex] })}:`);
|
|
116
|
+
console.log('============================================================');
|
|
117
|
+
console.log(logContent.slice(-2000)); // Show last 2000 characters
|
|
118
|
+
console.log('============================================================');
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
console.log(t('debug.noDebugLogsFound'));
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
console.log(t('debug.debugLogsDirectoryNotFound'));
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error(t('errors.errorReadingDebugLogs', { error: error.message }));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
await this.manager.prompt('\n' + t('menu.pressEnterToContinue'));
|
|
131
|
+
await this.manager.showInteractiveMenu();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Alias for showDebugMenu for backward compatibility
|
|
136
|
+
*/
|
|
137
|
+
async show() {
|
|
138
|
+
return this.showDebugMenu();
|
|
139
|
+
}
|
|
140
|
+
};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive Menu Manager
|
|
3
|
+
* @module managers/InteractiveMenu
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { t } = require('../../../utils/i18n-helper');
|
|
7
|
+
|
|
8
|
+
module.exports = class InteractiveMenu {
|
|
9
|
+
constructor(manager) {
|
|
10
|
+
this.manager = manager; // Reference to I18nManager for access to methods and properties
|
|
11
|
+
this.adminAuth = manager.adminAuth;
|
|
12
|
+
this.ui = manager.ui;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Display the main interactive menu with 13 options
|
|
17
|
+
*/
|
|
18
|
+
async showInteractiveMenu() {
|
|
19
|
+
// Check if we're in non-interactive mode (like echo 0 | node script)
|
|
20
|
+
if (this.manager.isNonInteractiveMode()) {
|
|
21
|
+
console.log(`\n${t('menu.title')}`);
|
|
22
|
+
console.log(t('menu.separator'));
|
|
23
|
+
console.log(`1. ${t('menu.options.init')}`);
|
|
24
|
+
console.log(`2. ${t('menu.options.analyze')}`);
|
|
25
|
+
console.log(`3. ${t('menu.options.validate')}`);
|
|
26
|
+
console.log(`4. ${t('menu.options.usage')}`);
|
|
27
|
+
console.log(`5. ${t('menu.options.complete')}`);
|
|
28
|
+
console.log(`6. ${t('menu.options.sizing')}`);
|
|
29
|
+
console.log(`7. ${t('menu.options.fix')}`);
|
|
30
|
+
console.log(`8. ${t('menu.options.status')}`);
|
|
31
|
+
console.log(`9. ${t('menu.options.delete')}`);
|
|
32
|
+
console.log(`10. ${t('menu.options.settings')}`);
|
|
33
|
+
console.log(`11. ${t('menu.options.help')}`);
|
|
34
|
+
console.log(`12. ${t('menu.options.language')}`);
|
|
35
|
+
console.log(`13. ${t('menu.options.scanner')}`);
|
|
36
|
+
console.log(`0. ${t('menu.options.exit')}`);
|
|
37
|
+
|
|
38
|
+
console.log('\n' + t('menu.nonInteractiveModeWarning'));
|
|
39
|
+
console.log(t('menu.useDirectExecution'));
|
|
40
|
+
console.log(t('menu.useHelpForCommands'));
|
|
41
|
+
this.manager.safeClose();
|
|
42
|
+
process.exit(0);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log(`\n${t('menu.title')}`);
|
|
47
|
+
console.log(t('menu.separator'));
|
|
48
|
+
console.log(`1. ${t('menu.options.init')}`);
|
|
49
|
+
console.log(`2. ${t('menu.options.analyze')}`);
|
|
50
|
+
console.log(`3. ${t('menu.options.validate')}`);
|
|
51
|
+
console.log(`4. ${t('menu.options.usage')}`);
|
|
52
|
+
console.log(`5. ${t('menu.options.complete')}`);
|
|
53
|
+
console.log(`6. ${t('menu.options.sizing')}`);
|
|
54
|
+
console.log(`7. ${t('menu.options.fix')}`);
|
|
55
|
+
console.log(`8. ${t('menu.options.status')}`);
|
|
56
|
+
console.log(`9. ${t('menu.options.delete')}`);
|
|
57
|
+
console.log(`10. ${t('menu.options.settings')}`);
|
|
58
|
+
console.log(`11. ${t('menu.options.help')}`);
|
|
59
|
+
console.log(`12. ${t('menu.options.language')}`);
|
|
60
|
+
console.log(`13. ${t('menu.options.scanner')}`);
|
|
61
|
+
console.log(`0. ${t('menu.options.exit')}`);
|
|
62
|
+
|
|
63
|
+
const choice = await this.manager.prompt('\n' + t('menu.selectOptionPrompt'));
|
|
64
|
+
|
|
65
|
+
switch (choice.trim()) {
|
|
66
|
+
case '1':
|
|
67
|
+
await this.manager.executeCommand('init', {fromMenu: true});
|
|
68
|
+
break;
|
|
69
|
+
case '2':
|
|
70
|
+
await this.manager.executeCommand('analyze', {fromMenu: true});
|
|
71
|
+
break;
|
|
72
|
+
case '3':
|
|
73
|
+
await this.manager.executeCommand('validate', {fromMenu: true});
|
|
74
|
+
break;
|
|
75
|
+
case '4':
|
|
76
|
+
await this.manager.executeCommand('usage', {fromMenu: true});
|
|
77
|
+
break;
|
|
78
|
+
case '5':
|
|
79
|
+
await this.manager.executeCommand('complete', {fromMenu: true});
|
|
80
|
+
break;
|
|
81
|
+
case '6':
|
|
82
|
+
await this.manager.executeCommand('sizing', {fromMenu: true});
|
|
83
|
+
break;
|
|
84
|
+
case '7':
|
|
85
|
+
await this.manager.executeCommand('fix', {fromMenu: true});
|
|
86
|
+
break;
|
|
87
|
+
case '8':
|
|
88
|
+
// Check for PIN protection
|
|
89
|
+
const authRequired = await this.adminAuth.isAuthRequiredForScript('summaryReports');
|
|
90
|
+
if (authRequired) {
|
|
91
|
+
console.log(`\n${t('adminCli.protectedAccess')}`);
|
|
92
|
+
const cliHelper = require('../../../utils/cli-helper');
|
|
93
|
+
const pin = await cliHelper.promptPin(t('adminCli.enterPin') + ': ');
|
|
94
|
+
const isValid = await this.adminAuth.verifyPin(pin);
|
|
95
|
+
|
|
96
|
+
if (!isValid) {
|
|
97
|
+
console.log(t('adminCli.invalidPin'));
|
|
98
|
+
await this.manager.prompt(t('menu.pressEnterToContinue'));
|
|
99
|
+
await this.showInteractiveMenu();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log(t('adminCli.accessGranted'));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
console.log(t('summary.status.generating'));
|
|
107
|
+
try {
|
|
108
|
+
const summaryTool = require('../../i18ntk-summary');
|
|
109
|
+
const summary = new summaryTool();
|
|
110
|
+
await summary.run({ fromMenu: true });
|
|
111
|
+
console.log(t('summary.status.completed'));
|
|
112
|
+
|
|
113
|
+
// Check if we're in interactive mode before prompting
|
|
114
|
+
if (!this.manager.isNonInteractiveMode()) {
|
|
115
|
+
try {
|
|
116
|
+
await this.manager.prompt('\n' + t('debug.pressEnterToContinue'));
|
|
117
|
+
await this.showInteractiveMenu();
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.log(t('menu.returning'));
|
|
120
|
+
process.exit(0);
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
console.log(t('status.exitingCompleted'));
|
|
124
|
+
process.exit(0);
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error(t('common.errorGeneratingStatusSummary', { error: error.message }));
|
|
128
|
+
|
|
129
|
+
// Check if we're in interactive mode before prompting
|
|
130
|
+
if (!this.manager.isNonInteractiveMode()) {
|
|
131
|
+
try {
|
|
132
|
+
await this.manager.prompt('\n' + t('debug.pressEnterToContinue'));
|
|
133
|
+
await this.showInteractiveMenu();
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.log(t('menu.returning'));
|
|
136
|
+
process.exit(0);
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
console.log(t('common.errorExiting'));
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
case '9':
|
|
145
|
+
await this.manager.deleteReports();
|
|
146
|
+
break;
|
|
147
|
+
case '10':
|
|
148
|
+
await this.manager.showSettingsMenu();
|
|
149
|
+
break;
|
|
150
|
+
case '11':
|
|
151
|
+
this.manager.showHelp();
|
|
152
|
+
await this.manager.prompt(t('menu.returnToMainMenu'));
|
|
153
|
+
await this.showInteractiveMenu();
|
|
154
|
+
break;
|
|
155
|
+
case '12':
|
|
156
|
+
await this.manager.showLanguageMenu();
|
|
157
|
+
break;
|
|
158
|
+
case '13':
|
|
159
|
+
await this.manager.executeCommand('scanner', {fromMenu: true});
|
|
160
|
+
break;
|
|
161
|
+
case '0':
|
|
162
|
+
console.log(t('menu.goodbye'));
|
|
163
|
+
this.manager.safeClose();
|
|
164
|
+
process.exit(0);
|
|
165
|
+
default:
|
|
166
|
+
console.log(t('menu.invalidChoice'));
|
|
167
|
+
await this.showInteractiveMenu();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Alias for showInteractiveMenu for backward compatibility
|
|
173
|
+
*/
|
|
174
|
+
async show() {
|
|
175
|
+
return this.showInteractiveMenu();
|
|
176
|
+
}
|
|
177
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language Menu Manager
|
|
3
|
+
* @module managers/LanguageMenu
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { t } = require('../../../utils/i18n-helper');
|
|
7
|
+
|
|
8
|
+
module.exports = class LanguageMenu {
|
|
9
|
+
constructor(manager) {
|
|
10
|
+
this.manager = manager; // Reference to I18nManager for access to methods and properties
|
|
11
|
+
this.ui = manager.ui;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Display the language selection menu
|
|
16
|
+
*/
|
|
17
|
+
async showLanguageMenu() {
|
|
18
|
+
console.log(`\n${t('language.title')}`);
|
|
19
|
+
console.log(t('language.separator'));
|
|
20
|
+
console.log(t('language.current', { language: this.ui.getLanguageDisplayName(this.ui.getCurrentLanguage()) }));
|
|
21
|
+
console.log('\n' + t('language.available'));
|
|
22
|
+
|
|
23
|
+
this.ui.availableLanguages.forEach((lang, index) => {
|
|
24
|
+
const displayName = this.ui.getLanguageDisplayName(lang);
|
|
25
|
+
const current = lang === this.ui.getCurrentLanguage() ? ' ✓' : '';
|
|
26
|
+
console.log(t('language.languageOption', { index: index + 1, displayName, current }));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
console.log(`0. ${t('language.backToMainMenu')}`);
|
|
30
|
+
|
|
31
|
+
const choice = await this.manager.prompt('\n' + t('language.prompt'));
|
|
32
|
+
const choiceNum = parseInt(choice);
|
|
33
|
+
|
|
34
|
+
if (choiceNum === 0) {
|
|
35
|
+
await this.manager.showInteractiveMenu();
|
|
36
|
+
return;
|
|
37
|
+
} else if (choiceNum >= 1 && choiceNum <= this.ui.availableLanguages.length) {
|
|
38
|
+
const selectedLang = this.ui.availableLanguages[choiceNum - 1];
|
|
39
|
+
await this.ui.changeLanguage(selectedLang);
|
|
40
|
+
console.log(t('language.changed', { language: this.ui.getLanguageDisplayName(selectedLang) }));
|
|
41
|
+
|
|
42
|
+
// Force reload translations for the entire system
|
|
43
|
+
const { loadTranslations } = require('../../../utils/i18n-helper');
|
|
44
|
+
loadTranslations(selectedLang);
|
|
45
|
+
|
|
46
|
+
// Return to main menu with new language
|
|
47
|
+
await this.manager.prompt('\n' + t('language.pressEnterToContinue'));
|
|
48
|
+
await this.manager.showInteractiveMenu();
|
|
49
|
+
} else {
|
|
50
|
+
console.log(t('language.invalid'));
|
|
51
|
+
await this.manager.prompt('\n' + t('language.pressEnterToContinue'));
|
|
52
|
+
await this.showLanguageMenu();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Alias for showLanguageMenu for backward compatibility
|
|
58
|
+
*/
|
|
59
|
+
async show() {
|
|
60
|
+
return this.showLanguageMenu();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings Menu Manager
|
|
3
|
+
* @module managers/SettingsMenu
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { t } = require('../../../utils/i18n-helper');
|
|
7
|
+
|
|
8
|
+
module.exports = class SettingsMenu {
|
|
9
|
+
constructor(manager) {
|
|
10
|
+
this.manager = manager; // Reference to I18nManager for access to methods and properties
|
|
11
|
+
this.adminAuth = manager.adminAuth;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Display the settings management menu
|
|
16
|
+
*/
|
|
17
|
+
async showSettingsMenu() {
|
|
18
|
+
try {
|
|
19
|
+
// Check for PIN protection
|
|
20
|
+
const authRequired = await this.adminAuth.isAuthRequiredForScript('settingsMenu');
|
|
21
|
+
if (authRequired) {
|
|
22
|
+
console.log(`\n${t('adminPin.protectedAccess')}`);
|
|
23
|
+
const cliHelper = require('../../../utils/cli-helper');
|
|
24
|
+
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
25
|
+
const isValid = await this.adminAuth.verifyPin(pin);
|
|
26
|
+
|
|
27
|
+
if (!isValid) {
|
|
28
|
+
console.log(t('adminPin.invalidPin'));
|
|
29
|
+
await this.manager.prompt(t('menu.pressEnterToContinue'));
|
|
30
|
+
await this.manager.showInteractiveMenu();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log(t('adminPin.accessGranted'));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const SettingsCLI = require('../../../settings/settings-cli');
|
|
38
|
+
const settingsCLI = new SettingsCLI();
|
|
39
|
+
await settingsCLI.run();
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('❌ Error opening settings:', error.message);
|
|
42
|
+
await this.manager.prompt(t('menu.pressEnterToContinue'));
|
|
43
|
+
}
|
|
44
|
+
await this.manager.showInteractiveMenu();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Alias for showSettingsMenu for backward compatibility
|
|
49
|
+
*/
|
|
50
|
+
async show() {
|
|
51
|
+
return this.showSettingsMenu();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Service
|
|
3
|
+
* Handles PIN protection, admin authentication, and security checks
|
|
4
|
+
* @module services/AuthenticationService
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const AdminAuth = require('../../../utils/admin-auth');
|
|
8
|
+
|
|
9
|
+
module.exports = class AuthenticationService {
|
|
10
|
+
constructor(config = {}) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
this.settings = null;
|
|
13
|
+
this.configManager = null;
|
|
14
|
+
this.adminAuth = new AdminAuth();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Initialize the service with required dependencies
|
|
19
|
+
* @param {Object} configManager - Configuration manager instance
|
|
20
|
+
*/
|
|
21
|
+
initialize(configManager) {
|
|
22
|
+
this.configManager = configManager;
|
|
23
|
+
this.settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if authentication is required for the system
|
|
28
|
+
* @returns {Promise<boolean>} True if authentication is required
|
|
29
|
+
*/
|
|
30
|
+
async isAuthRequired() {
|
|
31
|
+
return await this.adminAuth.isAuthRequired();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Check if authentication is required for a specific script
|
|
36
|
+
* @param {string} scriptName - Name of the script to check
|
|
37
|
+
* @returns {Promise<boolean>} True if authentication is required
|
|
38
|
+
*/
|
|
39
|
+
async isAuthRequiredForScript(scriptName) {
|
|
40
|
+
return await this.adminAuth.isAuthRequiredForScript(scriptName);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Verify PIN for authentication
|
|
45
|
+
* @param {string} pin - PIN to verify
|
|
46
|
+
* @returns {Promise<boolean>} True if PIN is valid
|
|
47
|
+
*/
|
|
48
|
+
async verifyPin(pin) {
|
|
49
|
+
return await this.adminAuth.verifyPin(pin);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check admin authentication for protected operations
|
|
54
|
+
* @param {Object} ui - UI instance for translations (optional)
|
|
55
|
+
* @returns {Promise<boolean>} True if authentication passed
|
|
56
|
+
*/
|
|
57
|
+
async checkAdminAuth(ui = null) {
|
|
58
|
+
const isRequired = await this.isAuthRequired();
|
|
59
|
+
if (!isRequired) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check if admin PIN was provided via command line
|
|
64
|
+
const args = this.parseArgs();
|
|
65
|
+
if (args.adminPin) {
|
|
66
|
+
const isValid = await this.verifyPin(args.adminPin);
|
|
67
|
+
if (isValid) {
|
|
68
|
+
console.log(ui ? ui.t('adminCli.authSuccess') : 'Authentication successful');
|
|
69
|
+
return true;
|
|
70
|
+
} else {
|
|
71
|
+
console.log(ui ? ui.t('adminCli.invalidPin') : 'Invalid PIN');
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log(ui ? ui.t('adminCli.authRequired') : 'Admin authentication required');
|
|
77
|
+
const cliHelper = require('../../../utils/cli-helper');
|
|
78
|
+
const pin = await cliHelper.promptPin(ui ? ui.t('adminCli.enterPin') : 'Enter PIN: ');
|
|
79
|
+
const isValid = await this.verifyPin(pin);
|
|
80
|
+
|
|
81
|
+
if (!isValid) {
|
|
82
|
+
console.log(ui ? ui.t('adminCli.invalidPin') : 'Invalid PIN');
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log(ui ? ui.t('adminCli.authSuccess') : 'Authentication successful');
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Parse command line arguments for authentication-related flags
|
|
92
|
+
* @returns {Object} Parsed arguments
|
|
93
|
+
*/
|
|
94
|
+
parseArgs() {
|
|
95
|
+
const args = process.argv.slice(2);
|
|
96
|
+
const parsed = {};
|
|
97
|
+
|
|
98
|
+
args.forEach(arg => {
|
|
99
|
+
if (arg.startsWith('--')) {
|
|
100
|
+
const [key, value] = arg.substring(2).split('=');
|
|
101
|
+
const sanitizedKey = key?.trim();
|
|
102
|
+
const sanitizedValue = value !== undefined ? value.trim() : true;
|
|
103
|
+
|
|
104
|
+
switch (sanitizedKey) {
|
|
105
|
+
case 'admin-pin':
|
|
106
|
+
parsed.adminPin = sanitizedValue || '';
|
|
107
|
+
break;
|
|
108
|
+
case 'no-prompt':
|
|
109
|
+
parsed.noPrompt = true;
|
|
110
|
+
break;
|
|
111
|
+
default:
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return parsed;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Check if we're in non-interactive mode
|
|
122
|
+
* @returns {boolean} True if in non-interactive mode
|
|
123
|
+
*/
|
|
124
|
+
isNonInteractiveMode() {
|
|
125
|
+
return !process.stdin.isTTY || process.stdin.destroyed;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Prompt user for input with fallback for non-interactive mode
|
|
130
|
+
* @param {string} question - Question to ask
|
|
131
|
+
* @returns {Promise<string>} User input or empty string
|
|
132
|
+
*/
|
|
133
|
+
async prompt(question) {
|
|
134
|
+
const cliHelper = require('../../../utils/cli-helper');
|
|
135
|
+
// If interactive not available, return empty string to avoid hangs
|
|
136
|
+
if (!process.stdin.isTTY || process.stdin.destroyed) {
|
|
137
|
+
console.log('\n⚠️ Interactive input not available, using default response.');
|
|
138
|
+
return Promise.resolve('');
|
|
139
|
+
}
|
|
140
|
+
return cliHelper.prompt(`${question} `);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Safe method to close readline interface
|
|
145
|
+
*/
|
|
146
|
+
safeClose() {
|
|
147
|
+
// This would be implemented if we had a readline interface to close
|
|
148
|
+
// For now, it's a placeholder for compatibility
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get execution context based on options and environment
|
|
153
|
+
* @param {Object} options - Execution options
|
|
154
|
+
* @returns {Object} Execution context information
|
|
155
|
+
*/
|
|
156
|
+
getExecutionContext(options = {}) {
|
|
157
|
+
// Check if called from interactive menu
|
|
158
|
+
if (options.fromMenu === true) {
|
|
159
|
+
return { type: 'manager', source: 'interactive_menu' };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Check if called from workflow/autorun
|
|
163
|
+
if (options.fromWorkflow === true) {
|
|
164
|
+
return { type: 'workflow', source: 'autorun_script' };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Check if this is a direct command line execution
|
|
168
|
+
if (process.argv.some(arg => arg.startsWith('--command='))) {
|
|
169
|
+
return { type: 'direct', source: 'command_line' };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Default to direct execution
|
|
173
|
+
return { type: 'direct', source: 'unknown' };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Execute command with authentication checks
|
|
178
|
+
* @param {string} command - Command to execute
|
|
179
|
+
* @param {Object} options - Execution options
|
|
180
|
+
* @param {Object} ui - UI instance for translations (optional)
|
|
181
|
+
* @returns {Promise<boolean>} True if command should proceed
|
|
182
|
+
*/
|
|
183
|
+
async executeCommandWithAuth(command, options = {}, ui = null) {
|
|
184
|
+
console.log(ui ? ui.t('menu.executingCommand', { command }) : `Executing command: ${command}`);
|
|
185
|
+
|
|
186
|
+
// Enhanced context detection
|
|
187
|
+
const executionContext = this.getExecutionContext(options);
|
|
188
|
+
const isDirectCommand = executionContext.type === 'direct';
|
|
189
|
+
const isWorkflowExecution = executionContext.type === 'workflow';
|
|
190
|
+
const isManagerExecution = executionContext.type === 'manager';
|
|
191
|
+
|
|
192
|
+
// Check admin authentication for all commands when PIN protection is enabled
|
|
193
|
+
const authRequiredCommands = ['init', 'analyze', 'validate', 'usage', 'scanner', 'complete', 'fix', 'sizing', 'workflow', 'status', 'delete', 'settings', 'debug'];
|
|
194
|
+
if (authRequiredCommands.includes(command)) {
|
|
195
|
+
const authPassed = await this.checkAdminAuth(ui);
|
|
196
|
+
if (!authPassed) {
|
|
197
|
+
if (!this.isNonInteractiveMode() && !isDirectCommand) {
|
|
198
|
+
await this.prompt(ui ? ui.t('menu.pressEnterToContinue') : 'Press Enter to continue...');
|
|
199
|
+
}
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Handle command completion based on execution context
|
|
209
|
+
* @param {Object} executionContext - Execution context information
|
|
210
|
+
* @param {Object} ui - UI instance for translations (optional)
|
|
211
|
+
* @returns {Promise<void>}
|
|
212
|
+
*/
|
|
213
|
+
async handleCommandCompletion(executionContext, ui = null) {
|
|
214
|
+
const isDirectCommand = executionContext.type === 'direct';
|
|
215
|
+
const isWorkflowExecution = executionContext.type === 'workflow';
|
|
216
|
+
const isManagerExecution = executionContext.type === 'manager';
|
|
217
|
+
|
|
218
|
+
console.log(ui ? ui.t('operations.completed') : 'Operation completed');
|
|
219
|
+
|
|
220
|
+
if (isManagerExecution && !this.isNonInteractiveMode()) {
|
|
221
|
+
// Interactive menu execution - return to menu
|
|
222
|
+
await this.prompt(ui ? ui.t('menu.returnToMainMenu') : 'Press Enter to return to main menu...');
|
|
223
|
+
} else {
|
|
224
|
+
// Direct commands, non-interactive mode, or workflow execution - exit immediately
|
|
225
|
+
console.log(ui ? ui.t('workflow.exitingCompleted') : 'Exiting...');
|
|
226
|
+
this.safeClose();
|
|
227
|
+
process.exit(0);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Handle command error based on execution context
|
|
233
|
+
* @param {Error} error - Error that occurred
|
|
234
|
+
* @param {Object} executionContext - Execution context information
|
|
235
|
+
* @param {Object} ui - UI instance for translations (optional)
|
|
236
|
+
* @returns {Promise<void>}
|
|
237
|
+
*/
|
|
238
|
+
async handleCommandError(error, executionContext, ui = null) {
|
|
239
|
+
const isDirectCommand = executionContext.type === 'direct';
|
|
240
|
+
const isWorkflowExecution = executionContext.type === 'workflow';
|
|
241
|
+
const isManagerExecution = executionContext.type === 'manager';
|
|
242
|
+
|
|
243
|
+
if (ui && ui.t) {
|
|
244
|
+
console.error(ui.t('common.errorExecutingCommand', { error: error.message }));
|
|
245
|
+
} else {
|
|
246
|
+
console.error(`Error executing command: ${error.message}`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (isManagerExecution && !this.isNonInteractiveMode()) {
|
|
250
|
+
// Interactive menu execution - show error and return to menu
|
|
251
|
+
await this.prompt(ui ? ui.t('menu.pressEnterToContinue') : 'Press Enter to continue...');
|
|
252
|
+
} else if (isDirectCommand && !this.isNonInteractiveMode()) {
|
|
253
|
+
// Direct command execution - show "enter to continue" and exit with error
|
|
254
|
+
await this.prompt(ui ? ui.t('menu.pressEnterToContinue') : 'Press Enter to continue...');
|
|
255
|
+
this.safeClose();
|
|
256
|
+
process.exit(1);
|
|
257
|
+
} else {
|
|
258
|
+
// Non-interactive mode or workflow execution - exit immediately with error
|
|
259
|
+
this.safeClose();
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
};
|