@@ -25,6 +25,7 @@ const BROWSER_UA = Platform.select({
2525
2626type LoginView = 'password' | 'phone' | 'oauthWeb' ;
2727type WebOAuthMode = 'github' | 'baizhiBridge' ;
28+ type BaizhiBridgeState = { targetBaseUrl : string ; phoneToSave ?: string ; authorizeViaFetch ?: boolean } ;
2829
2930function hostOf ( url : string ) : string {
3031 return url . match ( / ^ h t t p s ? : \/ \/ ( [ ^ / ? # ] + ) / i) ?. [ 1 ] ?. toLowerCase ( ) || '' ;
@@ -83,6 +84,21 @@ function isGithubCallback(url: string): boolean {
8384 return url . startsWith ( GITHUB_CALLBACK_URL ) || url . startsWith ( 'monkeycode:///oauth/github' ) ;
8485}
8586
87+ async function readResponseError ( res : Response , fallback : string ) : Promise < string > {
88+ try {
89+ const json = ( await res . clone ( ) . json ( ) ) as { message ?: string } ;
90+ if ( json ?. message ) return json . message . replace ( / \s * \[ t r a c e _ i d : [ ^ \] ] + \] \s * $ / i, '' ) . trim ( ) ;
91+ } catch {
92+ try {
93+ const text = await res . text ( ) ;
94+ if ( text . trim ( ) ) return text . trim ( ) ;
95+ } catch {
96+ /* keep fallback */
97+ }
98+ }
99+ return fallback ;
100+ }
101+
86102export default function LoginScreen ( ) {
87103 const t = useTheme ( ) ;
88104 const {
@@ -127,7 +143,7 @@ export default function LoginScreen() {
127143 const baizhiBridgeLeftRef = useRef ( false ) ;
128144 const baizhiBridgeDoneRef = useRef ( false ) ;
129145 const baizhiAutoAuthorizeUrlRef = useRef ( '' ) ;
130- const baizhiBridgeRef = useRef < { targetBaseUrl : string ; phoneToSave ?: string } | null > ( null ) ;
146+ const baizhiBridgeRef = useRef < BaizhiBridgeState | null > ( null ) ;
131147
132148 // 手机号 / 抖音 / GitHub 登录入口只在官方云展示;私有化 / 自定义地址保持账号密码入口。
133149 const cloud = norm ( serverUrl || baseUrl ) === DEFAULT_BASE_URL ;
@@ -209,9 +225,9 @@ export default function LoginScreen() {
209225 return target ;
210226 } , [ baseUrl , basicAuth , basicAuthInput , serverUrl , updateBaseUrl , updateBasicAuth ] ) ;
211227
212- const startBaizhiBridge = useCallback ( ( title : string , targetBaseUrl : string , phoneToSave ?: string ) => {
228+ const startBaizhiBridge = useCallback ( ( title : string , targetBaseUrl : string , phoneToSave ?: string , authorizeViaFetch = false ) => {
213229 const cleanTarget = norm ( targetBaseUrl || baseUrl || DEFAULT_BASE_URL ) ;
214- baizhiBridgeRef . current = { targetBaseUrl : cleanTarget , phoneToSave } ;
230+ baizhiBridgeRef . current = { targetBaseUrl : cleanTarget , phoneToSave, authorizeViaFetch } ;
215231 baizhiBridgeLeftRef . current = false ;
216232 baizhiBridgeDoneRef . current = false ;
217233 baizhiAutoAuthorizeUrlRef . current = '' ;
@@ -327,7 +343,7 @@ export default function LoginScreen() {
327343 const result = await authorizeDouyin ( ) ;
328344 setPhase ( '正在完成抖音登录…' ) ;
329345 await startDouyinAppBaizhiLogin ( result . code ) ;
330- startBaizhiBridge ( '抖音登录' , targetBaseUrl ) ;
346+ startBaizhiBridge ( '抖音登录' , targetBaseUrl , undefined , true ) ;
331347 } catch ( e ) {
332348 if ( ( e as { code ?: string } ) ?. code === 'E_DOUYIN_CANCELLED' ) return ;
333349 setError ( formatError ( e , '抖音登录失败,请重试' ) ) ;
@@ -406,7 +422,7 @@ export default function LoginScreen() {
406422 try {
407423 const targetBaseUrl = await applyServerSettings ( ) ;
408424 await startBaizhiPhoneLogin ( cleanPhone , cleanCode ) ;
409- startBaizhiBridge ( '手机号登录' , targetBaseUrl , cleanPhone ) ;
425+ startBaizhiBridge ( '手机号登录' , targetBaseUrl , cleanPhone , true ) ;
410426 } catch ( e ) {
411427 setError ( formatError ( e , '登录失败,请重试' ) ) ;
412428 } finally {
@@ -427,17 +443,49 @@ export default function LoginScreen() {
427443 setView ( nextView ) ;
428444 } ;
429445
446+ const authorizeBaizhiWithNativeSession = useCallback ( async ( apiUrl : string ) => {
447+ const bridge = baizhiBridgeRef . current ;
448+ if ( ! bridge || baizhiBridgeDoneRef . current ) return ;
449+ setError ( '' ) ;
450+ setBusy ( true ) ;
451+ setPhase ( '正在确认授权…' ) ;
452+ try {
453+ const res = await fetch ( apiUrl , { credentials : 'include' , redirect : 'follow' } ) ;
454+ if ( ! res . ok ) {
455+ throw new ApiError ( await readResponseError ( res , `百智云授权失败(${ res . status } )` ) , undefined , res . status ) ;
456+ }
457+ await finishBaizhiBridgeLogin ( ) ;
458+ } catch ( e ) {
459+ baizhiBridgeDoneRef . current = false ;
460+ baizhiBridgeLeftRef . current = false ;
461+ baizhiAutoAuthorizeUrlRef . current = '' ;
462+ baizhiBridgeRef . current = null ;
463+ setWebOAuthUrl ( '' ) ;
464+ setWebOAuthTitle ( '' ) ;
465+ setWebOAuthMode ( 'github' ) ;
466+ setView ( bridge . phoneToSave ? 'phone' : 'password' ) ;
467+ setError ( formatError ( e , '登录失败,请重试' ) ) ;
468+ } finally {
469+ setBusy ( false ) ;
470+ setPhase ( '' ) ;
471+ }
472+ } , [ finishBaizhiBridgeLogin ] ) ;
473+
430474 const openBaizhiAuthorizeApi = useCallback ( ( url : string ) => {
431475 if ( webOAuthMode !== 'baizhiBridge' ) return false ;
432476 const apiUrl = toBaizhiAuthorizeApiUrl ( url ) ;
433477 if ( ! apiUrl ) return false ;
434478 if ( baizhiAutoAuthorizeUrlRef . current === apiUrl ) return true ;
435479 baizhiAutoAuthorizeUrlRef . current = apiUrl ;
436- setPhase ( '正在确认授权…' ) ;
437- setWebOAuthUrl ( apiUrl ) ;
438- setWebOAuthKey ( ( k ) => k + 1 ) ;
480+ if ( baizhiBridgeRef . current ?. authorizeViaFetch ) {
481+ void authorizeBaizhiWithNativeSession ( apiUrl ) ;
482+ } else {
483+ setPhase ( '正在确认授权…' ) ;
484+ setWebOAuthUrl ( apiUrl ) ;
485+ setWebOAuthKey ( ( k ) => k + 1 ) ;
486+ }
439487 return true ;
440- } , [ webOAuthMode ] ) ;
488+ } , [ authorizeBaizhiWithNativeSession , webOAuthMode ] ) ;
441489
442490 const onWebOAuthNav = useCallback (
443491 ( navState : WebViewNavigation ) => {
0 commit comments