I'm trying to build an Angular application hosted on my GitHub pages with a custom verified domain.
This is my code to call GitHub API and get the zip archive of a (public) repository of mine:
public getTemplate() {
return this.http.get(
'https://api.github.com/repos/crystal-nest/cobweb-mod-template/zipball/1.20.4',
{
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
}
);
}
I know the URL is correct because if I paste it in my browser the download starts.
However, when the code gets executed I get the following error:
Access to XMLHttpRequest at 'https://codeload.github.com/Crystal-Nest/cobweb-mod-template/legacy.zip/refs/heads/1.20.4' (redirected from 'https://api.github.com/repos/crystal-nest/cobweb-mod-template/zipball/1.20.4') from origin 'https://crystalnest.it' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I then tried with JSONP requests:
return this.http.jsonp('https://api.github.com/repos/crystal-nest/cobweb-mod-template/zipball/1.20.4', 'callback');
But this time the error I received was:
{
"headers": {
"normalizedNames": {},
"lazyUpdate": null,
"headers": {}
},
"status": 0,
"statusText": "JSONP Error",
"url": "https://api.github.com/repos/crystal-nest/cobweb-mod-template/zipball/1.20.4?callback=ng_jsonp_callback_0",
"ok": false,
"name": "HttpErrorResponse",
"message": "Http failure response for https://api.github.com/repos/crystal-nest/cobweb-mod-template/zipball/1.20.4?callback=ng_jsonp_callback_0: 0 JSONP Error",
"error": {
"message": "JSONP injected script did not invoke callback.",
"stack": "Error: JSONP injected script did not invoke callback.\n stacktrace omitted for the sake of brevity"
}
}
My code can be found here, and here there's a live example.
Side note: even if the request worked and I was able to get the file as an 'arraybuffer'
(the data I need to process the file with JSZip), it's a pain that the zip name and its root directory have a code appended at the end (that I can't understand what it is and thus I can't predict what it's going to be). Ideally, I'd like to use this URL https://github.com/crystal-nest/cobweb-mod-template/archive/refs/heads/1.20.4.zip
because it gives me a name easy to predict and thus easy to process.
As pointed out on GitHub community forum here, the only current way to do it is by employing a proxy.
In my case, I simply used this free and public proxy.
As pointed out, that proxy is not free anymore.
I ended up using a custom reverse proxy hosted for free on a Cloudflare worker.
This is the code:
/**
* Reverse proxy for GitHub repository zip archive downloads.
*/
export default {
async fetch(request, env, ctx) {
if (new URL(request.url).hostname == 'crystalnest.it') {
const {user, repo, branch} = await request.json();
return new Response(
await (await fetch(`https://codeload.github.com/${user}/${repo}/zip/refs/heads/${branch}`)).arrayBuffer(),
{
headers: {
"Access-Control-Allow-Headers": request.headers.get("Access-Control-Request-Headers"),
"Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS",
"Access-Control-Allow-Origin": "*",
"Access-Control-Max-Age": "300",
"content-type": "application/zip",
}
}
);
}
return new Response(`Forbidden request URL: ${request.url}`, {status: 403, statusText: 'Forbidden request URL.'});
},
};
This worker is then accessible from a custom route based on my domain.