Skip to content

Commit f46d26e

Browse files
Merge pull request #25 from exelearning/22-avoid-port-conflicts-and-fix-contrast-problem
Add CORS proxy + Publish Docker images
2 parents 51fed34 + 5b84b32 commit f46d26e

2 files changed

Lines changed: 45 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# CHANGELOG
22

3-
## v4.0.0-rc1 – 2026-04-14
3+
## v4.0.0 – 2026-04-27
44

55
- Version jump to 4.0.0 to align numbering with eXeLearning for consistency across related projects.
66
- Add Docker image (published to GitHub Container Registry) for optional containerized deployment.
@@ -9,6 +9,7 @@
99
- Improve Web Worker lifecycle management by properly terminating the worker, clearing its reference and standardizing error reporting.
1010
- Align license declaration for consistency across the project.
1111
- Improve error handling and user feedback for storage-related failures, including insufficient storage space conditions.
12+
- Add `github-proxy.exelearning.dev` as the primary CORS proxy fallback for GitHub URL downloads, improving reliability when loading files from CORS-restricted sources.
1213

1314
---
1415

js/app.js

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,16 @@
613613
return urlObj.toString();
614614
}
615615

616+
// GitHub blob viewer: convert to raw content URL
617+
// Format: https://github.com/{user}/{repo}/blob/{ref}/{path}
618+
// Convert to: https://raw.githubusercontent.com/{user}/{repo}/{ref}/{path}
619+
if (urlObj.hostname === 'github.com') {
620+
const match = urlObj.pathname.match(/^\/([^/]+)\/([^/]+)\/blob\/(.+)$/);
621+
if (match) {
622+
return `https://raw.githubusercontent.com/${match[1]}/${match[2]}/${match[3]}`;
623+
}
624+
}
625+
616626
// Return original URL if no conversion needed
617627
return url;
618628
} catch (e) {
@@ -696,11 +706,30 @@
696706
* Each proxy has a different URL format
697707
*/
698708
const CORS_PROXIES = [
709+
{ url: 'https://github-proxy.exelearning.dev/?url=', encode: true, githubOnly: true },
699710
{ url: 'https://corsproxy.io/?', encode: true },
700711
{ url: 'https://api.allorigins.win/raw?url=', encode: true },
701712
{ url: 'https://cors.eu.org/', encode: false }
702713
];
703714

715+
const GITHUB_HOSTNAMES = new Set([
716+
'github.com',
717+
'raw.githubusercontent.com',
718+
'gist.githubusercontent.com',
719+
'objects.githubusercontent.com',
720+
'codeload.github.com',
721+
'releases.githubusercontent.com',
722+
'media.githubusercontent.com'
723+
]);
724+
725+
function isGithubUrl(url) {
726+
try {
727+
return GITHUB_HOSTNAMES.has(new URL(url).hostname);
728+
} catch {
729+
return false;
730+
}
731+
}
732+
704733
/**
705734
* Fetch with CORS proxy fallback
706735
* @param {string} url - The URL to fetch
@@ -717,25 +746,34 @@
717746
}
718747

719748
// Try each proxy until one works
749+
const isGithub = isGithubUrl(url);
720750
let lastError;
721751
for (const proxy of CORS_PROXIES) {
722-
try {
723-
const proxyUrl = proxy.encode
724-
? proxy.url + encodeURIComponent(url)
725-
: proxy.url + url;
752+
if (proxy.githubOnly && !isGithub) continue;
753+
754+
const proxyUrl = proxy.encode
755+
? proxy.url + encodeURIComponent(url)
756+
: proxy.url + url;
726757

758+
const controller = new AbortController();
759+
const timer = setTimeout(() => controller.abort(), 8000);
760+
761+
try {
727762
console.log(`[App] Trying proxy: ${proxy.url}`);
728763
const response = await fetch(proxyUrl, {
729764
method: 'GET',
730765
mode: 'cors',
731-
credentials: 'omit'
766+
credentials: 'omit',
767+
signal: controller.signal
732768
});
769+
clearTimeout(timer);
733770

734771
if (response.ok) {
735772
console.log(`[App] Proxy ${proxy.url} succeeded`);
736773
return response;
737774
}
738775
} catch (err) {
776+
clearTimeout(timer);
739777
console.warn(`[App] Proxy ${proxy.url} failed:`, err.message);
740778
lastError = err;
741779
}

0 commit comments

Comments
 (0)