@@ -767,6 +767,55 @@ function setupSyncFeature() {
767767
768768 const API_BASE = '/api' ;
769769 let availableStructure = { } ; // Store the loaded structure
770+ let turnstileSiteKey = '' ;
771+ let turnstileWidgetId = null ;
772+
773+ // 獲取 Turnstile site key
774+ async function fetchTurnstileSiteKey ( ) {
775+ if ( turnstileSiteKey ) return turnstileSiteKey ;
776+ try {
777+ const res = await fetch ( `${ API_BASE } /turnstile-site-key` ) ;
778+ const data = await res . json ( ) ;
779+ turnstileSiteKey = data . siteKey || '' ;
780+ } catch ( e ) {
781+ console . warn ( 'Failed to fetch Turnstile site key:' , e ) ;
782+ }
783+ return turnstileSiteKey ;
784+ }
785+
786+ // 渲染 Turnstile widget
787+ async function renderTurnstile ( ) {
788+ const container = document . getElementById ( 'turnstileContainer' ) ;
789+ if ( ! container ) return ;
790+
791+ // 先重置舊的 widget
792+ if ( turnstileWidgetId !== null && typeof turnstile !== 'undefined' ) {
793+ try { turnstile . remove ( turnstileWidgetId ) ; } catch ( e ) { /* ignore */ }
794+ turnstileWidgetId = null ;
795+ }
796+ container . innerHTML = '' ;
797+
798+ const siteKey = await fetchTurnstileSiteKey ( ) ;
799+ if ( ! siteKey ) return ;
800+
801+ // 等待 Turnstile API 載入
802+ const waitForTurnstile = ( ) => new Promise ( ( resolve ) => {
803+ if ( typeof turnstile !== 'undefined' ) return resolve ( ) ;
804+ const interval = setInterval ( ( ) => {
805+ if ( typeof turnstile !== 'undefined' ) {
806+ clearInterval ( interval ) ;
807+ resolve ( ) ;
808+ }
809+ } , 100 ) ;
810+ } ) ;
811+
812+ await waitForTurnstile ( ) ;
813+
814+ turnstileWidgetId = turnstile . render ( container , {
815+ sitekey : siteKey ,
816+ theme : 'dark' ,
817+ } ) ;
818+ }
770819
771820 // Helper to toggle modal
772821 const toggleModal = ( modal , show ) => {
@@ -812,16 +861,20 @@ function setupSyncFeature() {
812861 const handleLogin = async ( ) => {
813862 const username = usernameInput . value . trim ( ) ;
814863 const password = passwordInput . value . trim ( ) ;
815- const turnstileResponse = typeof turnstile !== 'undefined' ? turnstile . getResponse ( ) : null ;
816864
817865 if ( ! username || ! password ) {
818866 showStatus ( loginStatus , '請輸入帳號密碼' , 'error' ) ;
819867 return ;
820868 }
821869
822- if ( ! turnstileResponse ) {
823- showStatus ( loginStatus , '請完成驗證' , 'error' ) ;
824- return ;
870+ // 取得 Turnstile token
871+ let turnstileResponse = '' ;
872+ if ( typeof turnstile !== 'undefined' && turnstileWidgetId !== null ) {
873+ turnstileResponse = turnstile . getResponse ( turnstileWidgetId ) || '' ;
874+ if ( ! turnstileResponse ) {
875+ showStatus ( loginStatus , '請完成人機驗證' , 'error' ) ;
876+ return ;
877+ }
825878 }
826879
827880 showStatus ( loginStatus , '登入中...' , 'normal' ) ;
@@ -832,7 +885,7 @@ function setupSyncFeature() {
832885 method : 'POST' ,
833886 headers : { 'Content-Type' : 'application/json' } ,
834887 credentials : 'include' ,
835- body : JSON . stringify ( { username, password, turnstile_response : turnstileResponse } )
888+ body : JSON . stringify ( { username, password, 'cf-turnstile-response' : turnstileResponse } )
836889 } ) ;
837890 const data = await res . json ( ) ;
838891
@@ -846,11 +899,11 @@ function setupSyncFeature() {
846899 } , 500 ) ;
847900 } else {
848901 showStatus ( loginStatus , data . message || '登入失敗' , 'error' ) ;
849- if ( typeof turnstile !== 'undefined' ) turnstile . reset ( ) ;
902+ if ( typeof turnstile !== 'undefined' && turnstileWidgetId !== null ) turnstile . reset ( turnstileWidgetId ) ;
850903 }
851904 } catch ( error ) {
852905 showStatus ( loginStatus , '連線錯誤: ' + error . message , 'error' ) ;
853- if ( typeof turnstile !== 'undefined' ) turnstile . reset ( ) ;
906+ if ( typeof turnstile !== 'undefined' && turnstileWidgetId !== null ) turnstile . reset ( turnstileWidgetId ) ;
854907 } finally {
855908 confirmLogin . disabled = false ;
856909 }
@@ -885,6 +938,7 @@ function setupSyncFeature() {
885938 toggleModal ( selectExamModal , false ) ;
886939 toggleModal ( loginModal , true ) ;
887940 usernameInput . focus ( ) ;
941+ renderTurnstile ( ) ;
888942 return ;
889943 }
890944
0 commit comments