@@ -273,6 +273,15 @@ const GITHUB_BRANCH = import.meta.env.VITE_GITHUB_BRANCH || "main";
273273const getRawBaseUrl = ( path ) => `https://raw.githubusercontent.com/${ GITHUB_USERNAME } /${ GITHUB_REPO } /${ GITHUB_BRANCH } /${ path } /` ;
274274const getRawUrl = ( path ) => `https://raw.githubusercontent.com/${ GITHUB_USERNAME } /${ GITHUB_REPO } /${ GITHUB_BRANCH } /${ path } ` ;
275275
276+ const ensureTrailingSlash = ( value = '' ) => value . endsWith ( '/' ) ? value : `${ value } /` ;
277+ const stripLeadingSlash = ( value = '' ) => value . replace ( / ^ \/ + / , '' ) ;
278+
279+ const APP_BASE_URL = ensureTrailingSlash ( import . meta. env . BASE_URL || '/' ) ;
280+ const LOCAL_CONTENT_BASE = ensureTrailingSlash ( `${ APP_BASE_URL } content` ) ;
281+
282+ const getLocalBaseUrl = ( path = '' ) => ensureTrailingSlash ( `${ LOCAL_CONTENT_BASE } ${ stripLeadingSlash ( path ) } ` ) ;
283+ const getLocalUrl = ( path = '' ) => `${ LOCAL_CONTENT_BASE } ${ stripLeadingSlash ( path ) } ` ;
284+
276285const COURSE_DATA = [
277286 {
278287 id : 'module-1' ,
@@ -2886,49 +2895,51 @@ export default function App() {
28862895 if ( ! activeLesson . path ) {
28872896 setLessonContent ( activeLesson . fallbackContent ) ;
28882897 setFetchError ( null ) ;
2898+ setBasePath ( '' ) ;
28892899 return ;
28902900 }
28912901
28922902 const fetchContent = async ( ) => {
28932903 setContentLoading ( true ) ;
28942904 setFetchError ( null ) ;
2895- setBasePath ( getRawBaseUrl ( activeLesson . path ) ) ;
2905+ setBasePath ( '' ) ;
28962906
2897- const tryFetch = async ( filename ) => {
2898- const path = `${ activeLesson . path } /${ filename } ` ;
2899- const url = getRawUrl ( path ) ;
2900- try {
2901- const res = await fetch ( url ) ;
2902- if ( res . ok ) {
2903- return await res . text ( ) ;
2904- }
2905- return null ;
2906- } catch ( e ) {
2907- return null ;
2907+ const candidateFiles = [ 'README.MD' , 'README.md' , 'readme.md' , 'index.md' , 'index.MD' ] ;
2908+ const failureLogs = [ ] ;
2909+ const sources = [
2910+ {
2911+ label : '本地内容镜像' ,
2912+ basePath : getLocalBaseUrl ( activeLesson . path ) ,
2913+ getUrl : ( filename ) => getLocalUrl ( `${ activeLesson . path } /${ filename } ` )
2914+ } ,
2915+ {
2916+ label : 'GitHub Raw' ,
2917+ basePath : getRawBaseUrl ( activeLesson . path ) ,
2918+ getUrl : ( filename ) => getRawUrl ( `${ activeLesson . path } /${ filename } ` )
29082919 }
2909- } ;
2920+ ] ;
29102921
29112922 try {
2912- let text = await tryFetch ( 'README.MD' ) ;
2913- if ( ! text ) {
2914- console . log ( "README.MD not found, trying README.md..." ) ;
2915- text = await tryFetch ( 'README.md' ) ;
2916- }
2917- if ( ! text ) {
2918- console . log ( "README.md not found, trying readme.md..." ) ;
2919- text = await tryFetch ( 'readme.md' ) ;
2923+ for ( const source of sources ) {
2924+ for ( const filename of candidateFiles ) {
2925+ const url = source . getUrl ( filename ) ;
2926+ try {
2927+ const res = await fetch ( url , { cache : 'no-store' } ) ;
2928+ if ( res . ok ) {
2929+ const text = await res . text ( ) ;
2930+ setLessonContent ( text ) ;
2931+ setBasePath ( source . basePath ) ;
2932+ return ;
2933+ }
2934+ failureLogs . push ( `${ source . label } : ${ url } (${ res . status } )` ) ;
2935+ } catch ( error ) {
2936+ failureLogs . push ( `${ source . label } : ${ url } (${ error . message } )` ) ;
2937+ }
2938+ }
29202939 }
29212940
2922- if ( text ) {
2923- setLessonContent ( text ) ;
2924- } else {
2925- const failedUrl = getRawUrl ( `${ activeLesson . path } /README.MD` ) ;
2926- console . error ( `Failed to fetch from ${ failedUrl } ` ) ;
2927- setFetchError ( failedUrl ) ;
2928- setLessonContent ( activeLesson . fallbackContent ) ;
2929- }
2930- } catch ( err ) {
2931- console . error ( err ) ;
2941+ console . error ( 'Failed to load lesson content' , failureLogs ) ;
2942+ setFetchError ( failureLogs . join ( '\n' ) ) ;
29322943 setLessonContent ( activeLesson . fallbackContent ) ;
29332944 } finally {
29342945 setContentLoading ( false ) ;
@@ -3462,14 +3473,14 @@ export default function App() {
34623473 { contentLoading ? (
34633474 < div className = "flex flex-col items-center justify-center py-20 text-slate-400 gap-4" >
34643475 < Loader2 className = "w-10 h-10 text-cyan-400 animate-spin" />
3465- < p > 正在从 GitHub 抓取最新教程 ...</ p >
3476+ < p > 正在加载课程内容 ...</ p >
34663477 </ div >
34673478 ) : fetchError ? (
34683479 < div className = "p-6 bg-red-900/20 border border-red-500/30 rounded-xl text-center" >
34693480 < div className = "flex justify-center mb-4" > < AlertTriangle className = "w-8 h-8 text-red-400" /> </ div >
34703481 < h3 className = "text-lg font-bold text-red-400 mb-2" > 内容加载失败</ h3 >
3471- < p className = "text-slate-400 text-sm mb-4" > 无法从 GitHub 获取文件。请检查仓库路径是否正确 。</ p >
3472- < div className = "bg-black/30 p-3 rounded font-mono text-xs text-slate-500 break-all" > { fetchError } </ div >
3482+ < p className = "text-slate-400 text-sm mb-4" > 无法加载课程文本,已尝试本地镜像与 GitHub Raw 。</ p >
3483+ < div className = "bg-black/30 p-3 rounded font-mono text-xs text-slate-500 break-all whitespace-pre-wrap " > { fetchError } </ div >
34733484 </ div >
34743485 ) : (
34753486 < article className = "prose prose-invert prose-lg max-w-3xl mx-auto mb-12" >
0 commit comments