daodou-command 1.4.6 → 1.4.7

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.
@@ -63,7 +63,7 @@
63
63
  <option name="presentableId" value="Default" />
64
64
  <updated>1758879086844</updated>
65
65
  <workItem from="1758879088110" duration="1336000" />
66
- <workItem from="1763003382850" duration="4466000" />
66
+ <workItem from="1763003382850" duration="5386000" />
67
67
  </task>
68
68
  <servers />
69
69
  </component>
package/CHANGELOG.md CHANGED
@@ -6,11 +6,20 @@
6
6
  并且此项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
7
7
 
8
8
 
9
+ ## [1.4.7] - 2025-12-04
10
+
11
+ ### 修复
12
+ - 修复 `dao build` 命令中 Cookie 过期问题
13
+ - 实现自动捕获和更新 Jenkins 返回的 `Set-Cookie`
14
+ - 优化 HTTP 请求拦截器,自动管理 Cookie
15
+ - 增强构建过程中 Session 的稳定性
16
+
9
17
  ## [1.4.6] - 2025-11-13
10
18
 
11
19
  ### 修复
12
20
  - 修复版本更新提示逻辑,当前版本已是最新版本时不再显示更新提示
13
21
  - 优化 `getUpdateReminder()` 函数,添加当前版本与最新版本的实时比较
22
+ - 修正 Node.js 引擎版本要求为 `>=20.18.1`
14
23
 
15
24
  ## [1.4.5] - 2025-11-13
16
25
 
@@ -107,6 +107,40 @@ class BuildCommand {
107
107
  baseUrl: null,
108
108
  auth: null
109
109
  };
110
+
111
+ // 创建 axios 实例
112
+ this.axios = axios.create({
113
+ maxRedirects: 0,
114
+ validateStatus: s => s < 400 || s === 401 || s === 403 || s === 302
115
+ });
116
+
117
+ // 请求拦截器:自动添加 Cookie
118
+ this.axios.interceptors.request.use(config => {
119
+ const cookieString = this.browserAuth.getCookieString();
120
+ if (cookieString) {
121
+ config.headers.Cookie = cookieString;
122
+ }
123
+ // 确保有 User-Agent
124
+ if (!config.headers['User-Agent']) {
125
+ config.headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36';
126
+ }
127
+ return config;
128
+ });
129
+
130
+ // 响应拦截器:自动更新 Cookie
131
+ this.axios.interceptors.response.use(async response => {
132
+ const setCookie = response.headers['set-cookie'];
133
+ if (setCookie) {
134
+ await this.browserAuth.updateCookiesFromSetCookie(setCookie);
135
+ }
136
+ return response;
137
+ }, async error => {
138
+ // 即使是错误响应,也可能包含 set-cookie
139
+ if (error.response && error.response.headers && error.response.headers['set-cookie']) {
140
+ await this.browserAuth.updateCookiesFromSetCookie(error.response.headers['set-cookie']);
141
+ }
142
+ return Promise.reject(error);
143
+ });
110
144
  }
111
145
 
112
146
  async execute(options = {}) {
@@ -242,19 +276,10 @@ class BuildCommand {
242
276
  }
243
277
 
244
278
  async ensureJenkinsSession() {
245
- // 先用当前 cookie 访问 Jenkins 首页,确认是否有效
246
- const cookieString = this.browserAuth.cookies
247
- ? this.browserAuth.cookies.map(c => `${c.name}=${c.value}`).join('; ')
248
- : '';
249
279
  try {
250
- const resp = await axios.get(this.jenkinsConfig.baseUrl, {
251
- headers: {
252
- Cookie: cookieString,
253
- 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
254
- },
255
- maxRedirects: 0,
256
- validateStatus: s => s < 400 || s === 401 || s === 403 || s === 302
257
- });
280
+ // 使用拦截器自动带上 Cookie
281
+ const resp = await this.axios.get(this.jenkinsConfig.baseUrl);
282
+
258
283
  // 302 跳转到登录页、401/403 都视为未登录
259
284
  if (resp.status === 401 || resp.status === 403) throw new Error('未登录');
260
285
  if (resp.status === 302 && resp.headers.location && resp.headers.location.includes('casdoor')) throw new Error('未登录');
@@ -286,11 +311,10 @@ class BuildCommand {
286
311
  async triggerBuild(jobName, params) {
287
312
  try {
288
313
  const url = `${this.jenkinsConfig.baseUrl}job/${encodeURIComponent(jobName)}/buildWithParameters`;
289
- let headers = { ...this.jenkinsConfig.auth.headers };
314
+ let headers = {};
290
315
  // 1. 获取 crumb(防止 403)
291
316
  try {
292
- const crumbResp = await axios.get(`${this.jenkinsConfig.baseUrl}crumbIssuer/api/json`, {
293
- headers,
317
+ const crumbResp = await this.axios.get(`${this.jenkinsConfig.baseUrl}crumbIssuer/api/json`, {
294
318
  timeout: 10000
295
319
  });
296
320
  const { crumb, crumbRequestField } = crumbResp.data;
@@ -299,7 +323,7 @@ class BuildCommand {
299
323
  // 如果 crumb 获取失败,继续尝试(部分 Jenkins 关闭了 CSRF 防护)
300
324
  }
301
325
  // 2. 触发构建
302
- const response = await axios.post(url, null, {
326
+ const response = await this.axios.post(url, null, {
303
327
  headers,
304
328
  params: params,
305
329
  timeout: 30000
@@ -328,8 +352,7 @@ class BuildCommand {
328
352
  let count = 0;
329
353
  while (!buildNumber && count < 60) { // 最多等2分钟
330
354
  try {
331
- const response = await axios.get(`${this.jenkinsConfig.baseUrl}queue/item/${queueId}/api/json`, {
332
- ...this.jenkinsConfig.auth,
355
+ const response = await this.axios.get(`${this.jenkinsConfig.baseUrl}queue/item/${queueId}/api/json`, {
333
356
  timeout: 10000
334
357
  });
335
358
  const item = response.data;
@@ -363,8 +386,7 @@ class BuildCommand {
363
386
  let lastLogTime = 0;
364
387
  while (building) {
365
388
  try {
366
- const response = await axios.get(`${this.jenkinsConfig.baseUrl}job/${encodeURIComponent(jobName)}/${buildNumber}/api/json`, {
367
- ...this.jenkinsConfig.auth,
389
+ const response = await this.axios.get(`${this.jenkinsConfig.baseUrl}job/${encodeURIComponent(jobName)}/${buildNumber}/api/json`, {
368
390
  timeout: 10000
369
391
  });
370
392
  const buildInfo = response.data;
@@ -378,8 +400,7 @@ class BuildCommand {
378
400
  const now = Date.now();
379
401
  if (now - lastLogTime > 2900) {
380
402
  try {
381
- const logResponse = await axios.get(`${this.jenkinsConfig.baseUrl}job/${encodeURIComponent(jobName)}/${buildNumber}/consoleText`, {
382
- ...this.jenkinsConfig.auth,
403
+ const logResponse = await this.axios.get(`${this.jenkinsConfig.baseUrl}job/${encodeURIComponent(jobName)}/${buildNumber}/consoleText`, {
383
404
  timeout: 10000
384
405
  });
385
406
  const log = logResponse.data;
@@ -109,8 +109,53 @@ class BrowserAuth {
109
109
  }
110
110
 
111
111
  /**
112
- * 确保已登录并有可用 cookies
112
+ * set-cookie 更新 cookies
113
+ * @param {Array<string>} setCookies 响应头中的 set-cookie 数组
113
114
  */
115
+ async updateCookiesFromSetCookie(setCookies) {
116
+ if (!setCookies || !Array.isArray(setCookies)) return;
117
+
118
+ if (!this.cookies) this.cookies = [];
119
+
120
+ let changed = false;
121
+ setCookies.forEach(str => {
122
+ // 简单解析:取第一个分号前的部分作为 name=value
123
+ const firstPart = str.split(';')[0];
124
+ const [name, ...valueParts] = firstPart.split('=');
125
+ const value = valueParts.join('=');
126
+
127
+ if (name && value) {
128
+ const index = this.cookies.findIndex(c => c.name === name);
129
+ if (index !== -1) {
130
+ if (this.cookies[index].value !== value) {
131
+ this.cookies[index].value = value;
132
+ changed = true;
133
+ }
134
+ } else {
135
+ this.cookies.push({
136
+ name,
137
+ value,
138
+ domain: new URL(this.jenkinsUrl).hostname,
139
+ path: '/'
140
+ });
141
+ changed = true;
142
+ }
143
+ }
144
+ });
145
+
146
+ if (changed) {
147
+ await this.saveCookies();
148
+ }
149
+ }
150
+
151
+ /**
152
+ * 获取 Cookie 字符串
153
+ */
154
+ getCookieString() {
155
+ if (!this.cookies) return '';
156
+ return this.cookies.map(c => `${c.name}=${c.value}`).join('; ');
157
+ }
158
+
114
159
  async ensureLogin() {
115
160
  if (this.loadCookies()) {
116
161
  console.log(chalk.green('✅ 已加载保存的cookies'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "daodou-command",
3
- "version": "1.4.6",
3
+ "version": "1.4.7",
4
4
  "description": "刀豆命令行工具 - 自动化构建和部署",
5
5
  "main": "index.js",
6
6
  "bin": {