55 * SPDX-License-Identifier: Apache-2.0
66 */
77
8-
98import * as crypto from 'crypto' ;
109import * as http from 'http' ;
1110import { URL } from 'url' ;
12- import { exec } from 'child_process ' ;
11+ import open from 'open ' ;
1312import { appEvents , AppEvent } from '../utils/events.js' ;
1413
15-
1614// 功能实现: 飞书OAuth2认证集成
1715// 实现方案: 基于飞书开放平台OAuth2授权码模式
1816// 影响范围: 新增认证模块,集成到现有认证流程
@@ -59,10 +57,10 @@ export class FeishuAuthHandler {
5957 */
6058 public buildAuthUrl ( ) : string {
6159 const params = new URLSearchParams ( {
62- app_id : this . config . appId , // 飞书使用app_id参数
60+ app_id : this . config . appId , // 飞书使用app_id参数
6361 redirect_uri : this . config . redirectUri ,
6462 response_type : 'code' ,
65- scope : 'contact:user.employee_id:readonly' , // 使用正确的scope
63+ scope : 'contact:user.employee_id:readonly' , // 使用正确的scope
6664 state : this . state ,
6765 } ) ;
6866
@@ -111,7 +109,10 @@ export class FeishuAuthHandler {
111109
112110 // 如果端口改变了,需要更新配置
113111 if ( currentPort !== port ) {
114- const newRedirectUri = this . config . redirectUri . replace ( `:${ port } ` , `:${ currentPort } ` ) ;
112+ const newRedirectUri = this . config . redirectUri . replace (
113+ `:${ port } ` ,
114+ `:${ currentPort } ` ,
115+ ) ;
115116 this . config . redirectUri = newRedirectUri ;
116117 console . log ( `📝 重定向URI已更新为: ${ newRedirectUri } ` ) ;
117118 }
@@ -122,21 +123,30 @@ export class FeishuAuthHandler {
122123 console . log ( `🚀 正在打开浏览器进行飞书授权...` ) ;
123124
124125 // 自动打开浏览器
125- this . openBrowser ( authUrl ) ;
126+ void this . openBrowser ( authUrl ) ;
126127 } ) ;
127128
128129 this . server ! . on ( 'error' , ( err : any ) => {
129130 if ( err . code === 'EADDRINUSE' ) {
130- console . log ( `⚠️ 端口 ${ currentPort } 被占用,尝试端口 ${ currentPort + 1 } ` ) ;
131- if ( currentPort < 6709 ) { // 最多尝试10个端口 (6699-6709)
131+ console . log (
132+ `⚠️ 端口 ${ currentPort } 被占用,尝试端口 ${ currentPort + 1 } ` ,
133+ ) ;
134+ if ( currentPort < 6709 ) {
135+ // 最多尝试10个端口 (6699-6709)
132136 tryListen ( currentPort + 1 ) ;
133137 } else {
134138 this . cleanup ( ) ;
135- resolve ( { success : false , error : '无法找到可用端口 (6699-6709)' } ) ;
139+ resolve ( {
140+ success : false ,
141+ error : '无法找到可用端口 (6699-6709)' ,
142+ } ) ;
136143 }
137144 } else {
138145 this . cleanup ( ) ;
139- resolve ( { success : false , error : `服务器启动失败: ${ err . message } ` } ) ;
146+ resolve ( {
147+ success : false ,
148+ error : `服务器启动失败: ${ err . message } ` ,
149+ } ) ;
140150 }
141151 } ) ;
142152 } ;
@@ -154,27 +164,16 @@ export class FeishuAuthHandler {
154164 /**
155165 * 自动打开浏览器到指定URL
156166 */
157- private openBrowser ( url : string ) : void {
158- const platform = process . platform ;
159-
160- let command : string ;
161- if ( platform === 'win32' ) {
162- command = `start "" "${ url } "` ;
163- } else if ( platform === 'darwin' ) {
164- command = `open "${ url } "` ;
165- } else {
166- command = `xdg-open "${ url } "` ;
167+ private async openBrowser ( url : string ) : Promise < void > {
168+ try {
169+ await open ( url , { wait : false } ) ;
170+ console . log ( '✅ 浏览器已打开,请在飞书页面完成授权' ) ;
171+ } catch ( error ) {
172+ const message = error instanceof Error ? error . message : String ( error ) ;
173+ console . error ( `❌ 无法自动打开浏览器: ${ message } ` ) ;
174+ console . log ( '📋 请手动复制以下URL到浏览器中打开:' ) ;
175+ console . log ( `🔗 ${ url } ` ) ;
167176 }
168-
169- exec ( command , ( error : any ) => {
170- if ( error ) {
171- console . error ( `❌ 无法自动打开浏览器: ${ error . message } ` ) ;
172- console . log ( `📋 请手动复制以下URL到浏览器中打开:` ) ;
173- console . log ( `🔗 ${ url } ` ) ;
174- } else {
175- console . log ( `✅ 浏览器已打开,请在飞书页面完成授权` ) ;
176- }
177- } ) ;
178177 }
179178
180179 /**
@@ -184,7 +183,7 @@ export class FeishuAuthHandler {
184183 private async handleCallbackWithPlatCheck (
185184 reqUrl : URL ,
186185 res : http . ServerResponse ,
187- resolve : ( result : FeishuAuthResult ) => void
186+ resolve : ( result : FeishuAuthResult ) => void ,
188187 ) : Promise < void > {
189188 // 直接处理飞书认证回调,不再处理DeepVlab
190189 console . log ( '🔄 [FeishuAuth] 处理飞书认证回调(旧流程)' ) ;
@@ -197,7 +196,7 @@ export class FeishuAuthHandler {
197196 private async handleCallback (
198197 reqUrl : URL ,
199198 res : http . ServerResponse ,
200- resolve : ( result : FeishuAuthResult ) => void
199+ resolve : ( result : FeishuAuthResult ) => void ,
201200 ) : Promise < void > {
202201 const code = reqUrl . searchParams . get ( 'code' ) ;
203202 const state = reqUrl . searchParams . get ( 'state' ) ;
@@ -228,10 +227,10 @@ export class FeishuAuthHandler {
228227 const accessToken = await this . exchangeCodeForToken ( code ) ;
229228 this . sendSuccessResponse ( res ) ;
230229 this . cleanup ( ) ;
231- resolve ( {
232- success : true ,
230+ resolve ( {
231+ success : true ,
233232 accessToken,
234- nextStepUrl : this . config . nextStepUrl
233+ nextStepUrl : this . config . nextStepUrl ,
235234 } ) ;
236235 } catch ( error ) {
237236 const errorMsg = error instanceof Error ? error . message : '未知错误' ;
@@ -256,16 +255,15 @@ export class FeishuAuthHandler {
256255 // 修复策略: 回到标准OAuth2规范,移除重复的body构建逻辑
257256 // 影响范围: packages/cli/src/auth/feishuAuth.ts:231-262
258257 // 修复日期: 2025-01-26
259-
258+
260259 const formData = new URLSearchParams ( {
261260 grant_type : 'authorization_code' ,
262- client_id : this . config . appId , // 使用标准OAuth2参数名
261+ client_id : this . config . appId , // 使用标准OAuth2参数名
263262 client_secret : this . config . appSecret , // 使用标准OAuth2参数名
264263 code : code ,
265264 redirect_uri : this . config . redirectUri ,
266265 } ) ;
267266
268-
269267 const response = await fetch ( tokenUrl , {
270268 method : 'POST' ,
271269 headers : {
@@ -282,7 +280,9 @@ export class FeishuAuthHandler {
282280 if ( ! response . ok ) {
283281 const errorText = await response . text ( ) ;
284282 console . error ( '❌ exchangeCodeForToken: 错误响应内容:' , errorText ) ;
285- throw new Error ( `HTTP ${ response . status } : ${ response . statusText } \n响应内容: ${ errorText } ` ) ;
283+ throw new Error (
284+ `HTTP ${ response . status } : ${ response . statusText } \n响应内容: ${ errorText } ` ,
285+ ) ;
286286 }
287287
288288 const data = await response . json ( ) ;
@@ -293,17 +293,19 @@ export class FeishuAuthHandler {
293293 // 修复策略: OAuth2标准响应通常直接包含access_token,而不是通过code字段判断
294294 // 影响范围: packages/cli/src/auth/feishuAuth.ts:276-280
295295 // 修复日期: 2025-01-26
296-
296+
297297 // 检查OAuth2标准错误格式
298298 if ( data . error ) {
299- throw new Error ( `飞书OAuth2错误: ${ data . error } - ${ data . error_description || '' } ` ) ;
299+ throw new Error (
300+ `飞书OAuth2错误: ${ data . error } - ${ data . error_description || '' } ` ,
301+ ) ;
300302 }
301-
303+
302304 // 检查飞书特有的code字段(如果存在)
303305 if ( data . code !== undefined && data . code !== 0 ) {
304306 throw new Error ( `飞书API错误: ${ data . msg || data . error || '未知错误' } ` ) ;
305307 }
306-
308+
307309 // 检查是否有access_token
308310 if ( ! data . access_token ) {
309311 throw new Error ( '响应中缺少access_token字段' ) ;
@@ -399,13 +401,11 @@ export class FeishuAuthHandler {
399401 </body>
400402 </html>
401403 ` ;
402-
404+
403405 res . writeHead ( 200 , { 'Content-Type' : 'text/html; charset=utf-8' } ) ;
404406 res . end ( html ) ;
405407 }
406408
407-
408-
409409 /**
410410 * 发送错误响应
411411 * 功能实现: 飞书认证失败后显示友好的错误页面并自动关闭
@@ -477,7 +477,7 @@ export class FeishuAuthHandler {
477477 </body>
478478 </html>
479479 ` ;
480-
480+
481481 res . writeHead ( 400 , { 'Content-Type' : 'text/html; charset=utf-8' } ) ;
482482 res . end ( html ) ;
483483 }
@@ -501,13 +501,16 @@ export class FeishuAuthHandler {
501501export function createFeishuAuthHandler (
502502 appId : string ,
503503 appSecret : string ,
504- nextStepUrl ?: string
504+ nextStepUrl ?: string ,
505505) : FeishuAuthHandler {
506506 const config : FeishuAuthConfig = {
507507 appId,
508508 appSecret,
509- redirectUri : 'http://localhost:7863/callback' , // 使用与飞书应用配置匹配的回调地址
510- nextStepUrl : nextStepUrl || process . env . DEEPX_SERVER_URL || 'https://api-code.deepvlab.ai' ,
509+ redirectUri : 'http://localhost:7863/callback' , // 使用与飞书应用配置匹配的回调地址
510+ nextStepUrl :
511+ nextStepUrl ||
512+ process . env . DEEPX_SERVER_URL ||
513+ 'https://api-code.deepvlab.ai' ,
511514 } ;
512515
513516 return new FeishuAuthHandler ( config ) ;
0 commit comments