daodou-command 1.0.0

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.
@@ -0,0 +1,266 @@
1
+ const chalk = require('chalk');
2
+ const ora = require('ora');
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+ const { ConfigManager } = require('../utils/config');
6
+ const { LangFileManager } = require('../utils/lang-file-manager');
7
+ const TranslationService = require('../utils/translation');
8
+
9
+ class LangCommand {
10
+ constructor() {
11
+ this.configManager = new ConfigManager();
12
+ this.langFileManager = new LangFileManager();
13
+ this.translationService = new TranslationService();
14
+ this.config = this.configManager.getLangConfig();
15
+ }
16
+
17
+ /**
18
+ * 执行 add 命令(支持自动翻译)
19
+ */
20
+ async add(key, value, options = {}) {
21
+ if (!key) {
22
+ console.error(chalk.red('❌ 请提供键名'));
23
+ process.exit(1);
24
+ }
25
+
26
+ // 如果没有指定 value,则使用 key 作为 value
27
+ const finalValue = value || key;
28
+
29
+ const defaultLang = this.config.defaultLang || 'en';
30
+ const dir = options.dir || this.config.defaultDir || './public/locales';
31
+ const fileName = this.config.fileName || 'common.json';
32
+
33
+ console.log(chalk.blue('🔧 多语言文件管理工具启动中...\n'));
34
+
35
+ try {
36
+ // 获取语言列表
37
+ const languages = await this.getLanguages(dir, options.lang);
38
+
39
+ if (languages.length === 0) {
40
+ console.log(chalk.yellow('⚠️ 未找到任何语言目录'));
41
+ return;
42
+ }
43
+
44
+ console.log(chalk.green(`✅ 发现语言: ${languages.join(', ')}\n`));
45
+
46
+ // 开始翻译流程
47
+ console.log(chalk.blue('🌐 开始翻译流程...'));
48
+ console.log(chalk.blue(`📊 进度: 0/${languages.length} 语言完成\n`));
49
+
50
+ // 统计变量
51
+ let successCount = 0;
52
+ let skipCount = 0;
53
+ let failCount = 0;
54
+ const results = {};
55
+
56
+ // 顺序处理每个语言
57
+ for (let i = 0; i < languages.length; i++) {
58
+ const langCode = languages[i];
59
+ const filePath = path.join(dir, langCode, fileName);
60
+
61
+ // 显示分隔线
62
+ console.log(chalk.gray('────────────────────────────────────────'));
63
+
64
+ // 显示当前处理的语言
65
+ const isDefault = this.translationService.isDefaultLanguage(langCode, defaultLang);
66
+ const langDisplay = isDefault ? `${langCode} (默认语言)` : langCode;
67
+ console.log(chalk.blue(`📝 正在处理语言: ${langDisplay}`));
68
+
69
+ try {
70
+ // 步骤 1: 检查 key 是否存在
71
+ console.log(chalk.blue(` 🔍 步骤 1/${isDefault ? '3' : '4'}: 检查 key "${key}" 是否存在...`));
72
+
73
+ if (fs.existsSync(filePath)) {
74
+ const fileContent = fs.readFileSync(filePath, 'utf8');
75
+ const data = JSON.parse(fileContent);
76
+
77
+ if (data[key] !== undefined) {
78
+ console.log(chalk.yellow(` ⚠️ key "${key}" 已存在,跳过该语言`));
79
+ console.log(chalk.gray(` 📄 结果: 跳过`));
80
+ results[langCode] = { status: 'skipped', reason: 'key 已存在' };
81
+ skipCount++;
82
+ continue;
83
+ }
84
+ } else {
85
+ console.log(chalk.yellow(` ⚠️ 文件 ${filePath} 不存在,跳过该语言`));
86
+ console.log(chalk.gray(` 📄 结果: 跳过`));
87
+ results[langCode] = { status: 'skipped', reason: '文件不存在' };
88
+ skipCount++;
89
+ continue;
90
+ }
91
+
92
+ console.log(chalk.green(` ✅ key "${key}" 不存在,可以添加`));
93
+
94
+ let translatedValue = finalValue;
95
+
96
+ if (isDefault) {
97
+ // 默认语言直接写入
98
+ console.log(chalk.blue(` ⚡ 步骤 2/3: 默认语言直接写入...`));
99
+ console.log(chalk.green(` ✅ 步骤 3/3: 成功写入文件`));
100
+ console.log(chalk.gray(` 📄 结果: "${key}": "${translatedValue}"`));
101
+ } else {
102
+ // 其他语言需要翻译
103
+ const translationResult = await this.translationService.translateText(finalValue, langCode, defaultLang);
104
+
105
+ if (translationResult.success) {
106
+ translatedValue = translationResult.result;
107
+ console.log(chalk.green(` ✅ 步骤 4/4: 成功写入文件`));
108
+ console.log(chalk.gray(` 📄 结果: "${key}": "${translatedValue}"`));
109
+ } else {
110
+ console.log(chalk.gray(` 📄 结果: 跳过`));
111
+ results[langCode] = { status: 'skipped', reason: '翻译失败' };
112
+ skipCount++;
113
+ continue;
114
+ }
115
+ }
116
+
117
+ // 写入文件
118
+ await this.langFileManager.addKey(filePath, key, translatedValue);
119
+
120
+ results[langCode] = {
121
+ status: 'success',
122
+ value: translatedValue,
123
+ originalValue: finalValue
124
+ };
125
+ successCount++;
126
+
127
+ } catch (error) {
128
+ console.log(chalk.red(` ❌ 处理失败: ${error.message}`));
129
+ console.log(chalk.gray(` 📄 结果: 失败`));
130
+ results[langCode] = { status: 'failed', error: error.message };
131
+ failCount++;
132
+ }
133
+
134
+ // 显示进度
135
+ console.log(chalk.blue(`📊 进度: ${i + 1}/${languages.length} 语言完成`));
136
+
137
+ // 等待 0.5 秒后处理下一个语言(除了最后一个)
138
+ if (i < languages.length - 1) {
139
+ console.log(chalk.gray('\n⏳ 等待 0.5s 后处理下一个语言...\n'));
140
+ await this.translationService.delay(500);
141
+ }
142
+ }
143
+
144
+ // 显示最终结果
145
+ console.log(chalk.gray('\n────────────────────────────────────────'));
146
+ console.log(chalk.green('🎉 翻译流程完成!\n'));
147
+
148
+ console.log(chalk.blue('📊 最终统计:'));
149
+ console.log(chalk.green(` ✅ 成功: ${successCount} 个语言`));
150
+ console.log(chalk.yellow(` ⏭️ 跳过: ${skipCount} 个语言`));
151
+ console.log(chalk.red(` ❌ 失败: ${failCount} 个语言`));
152
+
153
+ // 显示代理统计信息
154
+ this.translationService.showProxyStats();
155
+
156
+ console.log(chalk.blue('\n📋 翻译结果汇总:'));
157
+ for (const [lang, result] of Object.entries(results)) {
158
+ if (result.status === 'success') {
159
+ console.log(chalk.green(` ${lang}: "${key}": "${result.value}"`));
160
+ } else if (result.status === 'skipped') {
161
+ console.log(chalk.yellow(` ${lang}: 跳过 (${result.reason})`));
162
+ } else {
163
+ console.log(chalk.red(` ${lang}: 失败 (${result.error})`));
164
+ }
165
+ }
166
+
167
+ } catch (error) {
168
+ console.error(chalk.red('❌ 操作失败:'), error.message);
169
+ process.exit(1);
170
+ }
171
+ }
172
+
173
+ /**
174
+ * 执行 remove 命令
175
+ */
176
+ async remove(key, options = {}) {
177
+ if (!key) {
178
+ console.error(chalk.red('❌ 请提供键名'));
179
+ process.exit(1);
180
+ }
181
+
182
+ const lang = options.lang || this.config.defaultLang || 'en';
183
+ const dir = options.dir || this.config.defaultDir || './public/locales';
184
+ const fileName = this.config.fileName || 'common.json';
185
+
186
+ console.log(chalk.blue('🔧 多语言文件管理工具启动中...\n'));
187
+
188
+ try {
189
+ // 获取语言列表
190
+ const languages = await this.getLanguages(dir, options.lang);
191
+
192
+ if (languages.length === 0) {
193
+ console.log(chalk.yellow('⚠️ 未找到任何语言目录'));
194
+ return;
195
+ }
196
+
197
+ console.log(chalk.green(`✅ 发现语言: ${languages.join(', ')}\n`));
198
+
199
+ // 批量处理每个语言
200
+ let successCount = 0;
201
+ let skipCount = 0;
202
+
203
+ for (const langCode of languages) {
204
+ const filePath = path.join(dir, langCode, fileName);
205
+
206
+ try {
207
+ const result = await this.langFileManager.removeKey(filePath, key);
208
+ if (result.success) {
209
+ console.log(chalk.green(`✅ 成功删除 "${key}" 从 ${langCode}`));
210
+ successCount++;
211
+ } else if (result.notFound) {
212
+ console.log(chalk.yellow(`⚠️ 跳过 "${key}" 在 ${langCode} (未找到)`));
213
+ skipCount++;
214
+ }
215
+ } catch (error) {
216
+ if (error.message.includes('目录不存在')) {
217
+ console.log(chalk.yellow(`⚠️ 跳过 ${langCode} (目录不存在)`));
218
+ } else if (error.message.includes('文件不存在')) {
219
+ console.log(chalk.yellow(`⚠️ 跳过 ${langCode} (文件不存在)`));
220
+ } else {
221
+ console.log(chalk.red(`❌ 处理 ${langCode} 失败: ${error.message}`));
222
+ }
223
+ }
224
+ }
225
+
226
+ // 显示统计结果
227
+ console.log(chalk.blue(`\n📊 处理完成: 成功 ${successCount} 个, 跳过 ${skipCount} 个`));
228
+
229
+ } catch (error) {
230
+ console.error(chalk.red('❌ 操作失败:'), error.message);
231
+ process.exit(1);
232
+ }
233
+ }
234
+
235
+ /**
236
+ * 获取语言列表
237
+ */
238
+ async getLanguages(dir, specifiedLang) {
239
+ // 如果指定了语言,只处理该语言
240
+ if (specifiedLang) {
241
+ return [specifiedLang];
242
+ }
243
+
244
+ // 否则扫描目录下的所有语言文件夹
245
+ try {
246
+ if (!fs.existsSync(dir)) {
247
+ console.log(chalk.yellow(`⚠️ 目录不存在: ${dir}`));
248
+ return [];
249
+ }
250
+
251
+ const items = fs.readdirSync(dir);
252
+ const languages = items.filter(item => {
253
+ const itemPath = path.join(dir, item);
254
+ return fs.statSync(itemPath).isDirectory();
255
+ });
256
+
257
+ console.log(chalk.blue(`🔍 扫描目录 ${dir}: 发现 ${languages.length} 个语言`));
258
+ return languages;
259
+ } catch (error) {
260
+ console.log(chalk.red(`❌ 扫描目录失败: ${error.message}`));
261
+ return [];
262
+ }
263
+ }
264
+ }
265
+
266
+ module.exports = new LangCommand();
@@ -0,0 +1,125 @@
1
+ const puppeteer = require('puppeteer');
2
+ const chalk = require('chalk');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+ const { ConfigManager } = require('./config');
7
+
8
+ // 配置管理器
9
+ const configManager = new ConfigManager();
10
+
11
+ class BrowserAuth {
12
+ constructor() {
13
+ this.config = configManager.getBuildConfig();
14
+ this.jenkinsUrl = this.config.jenkinsUrl;
15
+ this.username = this.config.jenkinsUsername;
16
+ this.password = this.config.jenkinsPassword;
17
+ this.cookies = null;
18
+ }
19
+
20
+ /**
21
+ * 启动浏览器并登录Jenkins
22
+ */
23
+ async login() {
24
+ try {
25
+ console.log(chalk.blue('🌐 启动浏览器...'));
26
+
27
+ // 验证 Jenkins URL
28
+ if (!this.jenkinsUrl || this.jenkinsUrl === 'your-jenkins-url') {
29
+ throw new Error('Jenkins URL 未配置或为模板值,请检查 .daodourc 文件');
30
+ }
31
+
32
+ this.browser = await puppeteer.launch({
33
+ headless: true,
34
+ // 移除硬编码路径,让 Puppeteer 自动查找
35
+ args: [
36
+ '--no-sandbox',
37
+ '--disable-setuid-sandbox',
38
+ '--disable-dev-shm-usage',
39
+ '--disable-accelerated-2d-canvas',
40
+ '--no-first-run',
41
+ '--no-zygote',
42
+ '--disable-gpu'
43
+ ]
44
+ });
45
+ this.page = await this.browser.newPage();
46
+ await this.page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
47
+ console.log(chalk.blue('🔗 访问Jenkins首页...'));
48
+ console.log(chalk.gray(` 目标URL: ${this.jenkinsUrl}`));
49
+ await this.page.goto(this.jenkinsUrl, { waitUntil: 'networkidle2', timeout: 30000 });
50
+ console.log(chalk.green(`✅ 当前页面: ${this.page.url()}`));
51
+
52
+ // 检查是否跳转到Casdoor登录页
53
+ if (this.page.url().includes('casdoor')) {
54
+ console.log(chalk.yellow('📋 检测到Casdoor登录页,自动填写登录信息...'));
55
+ // 等待用户名输入框
56
+ await this.page.waitForSelector('input[name="username"], input[type="text"]', { timeout: 10000 });
57
+ // 填写用户名
58
+ await this.page.type('input[name="username"], input[type="text"]', this.username, {delay: 50});
59
+ // 填写密码
60
+ await this.page.type('input[name="password"], input[type="password"]', this.password, {delay: 50});
61
+ // 点击登录按钮
62
+ await Promise.all([
63
+ this.page.click('button[type="submit"], input[type="submit"], .login-button'),
64
+ this.page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 30000 })
65
+ ]);
66
+ console.log(chalk.green(`✅ 登录表单已提交,当前页面: ${this.page.url()}`));
67
+ }
68
+
69
+ // 检查是否成功跳转到Jenkins
70
+ if (this.page.url().includes(this.jenkinsUrl)) {
71
+ console.log(chalk.green('🎉 Jenkins登录成功!'));
72
+ // 获取cookies
73
+ this.cookies = await this.page.cookies();
74
+ await this.saveCookies();
75
+ await this.browser.close();
76
+ return true;
77
+ } else {
78
+ console.log(chalk.red('❌ 登录失败,当前页面:'), this.page.url());
79
+ await this.browser.close();
80
+ throw new Error('自动登录失败,未跳转回Jenkins');
81
+ }
82
+ } catch (error) {
83
+ console.error(chalk.red('Jenkins自动登录失败:'), error.message);
84
+ if (this.browser) await this.browser.close();
85
+ throw error;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * 保存 cookies 到本地
91
+ */
92
+ async saveCookies() {
93
+ const dir = path.join(os.homedir(), '.daodou');
94
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir);
95
+ const file = path.join(dir, 'cookies.json');
96
+ fs.writeFileSync(file, JSON.stringify(this.cookies, null, 2));
97
+ }
98
+
99
+ /**
100
+ * 加载本地 cookies
101
+ */
102
+ loadCookies() {
103
+ const file = path.join(os.homedir(), '.daodou', 'cookies.json');
104
+ if (fs.existsSync(file)) {
105
+ this.cookies = JSON.parse(fs.readFileSync(file, 'utf-8'));
106
+ return true;
107
+ }
108
+ return false;
109
+ }
110
+
111
+ /**
112
+ * 确保已登录并有可用 cookies
113
+ */
114
+ async ensureLogin() {
115
+ if (this.loadCookies()) {
116
+ console.log(chalk.green('✅ 已加载保存的cookies'));
117
+ return;
118
+ }
119
+ await this.login();
120
+ }
121
+ }
122
+
123
+ module.exports = {
124
+ BrowserAuth
125
+ };