CloudFlare代理下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
'use strict'

/**
* static files (404.html, sw.js, conf.js)
*/
const ASSET_URL = 'https://b.godstick.ggff.net/' // 自定义资源URL
const PREFIX = '/' // 自定义路径前缀

const Config = {
jsdelivr: 0 // 暂不需要使用 jsDelivr 镜像
}

const whiteList = ['packages.element.io', 'www.sgjdog.site'] // 简化白名单,仅保留域名

/** @type {ResponseInit} */
const PREFLIGHT_INIT = {
status: 204,
headers: new Headers({
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS',
'access-control-max-age': '1728000',
}),
}

// 硬编码正则表达式,支持 b.godstick.ggff.net 代理的嵌套 URL
const exp1 = /^(?:https?:\/\/)?b\.godstick\.ggff\.net\/(https?:\/\/(?:www\.sgjdog\.site|packages\.element\.io)\/(?:.*\/)?[^\/]+\.(zip|iso|pdf|rar|mp3|mp4|mkv|tar|gz|tar\.gz|tgz|bz2|Z|exe))$/i;

/**
* @param {any} body
* @param {number} status
* @param {Object<string, string>} headers
*/
function makeRes(body, status = 200, headers = {}) {
headers['access-control-allow-origin'] = '*';
return new Response(body, { status, headers });
}

/**
* @param {string} urlStr
*/
function newUrl(urlStr) {
try {
return new URL(urlStr);
} catch (err) {
return null;
}
}

/**
* 检查 URL 是否匹配正则表达式
* @param {string} urlStr
*/
function checkUrl(urlStr) {
return exp1.test(urlStr);
}

addEventListener('fetch', e => {
const ret = fetchHandler(e)
.catch(err => makeRes('cfworker error:\n' + err.stack, 502));
e.respondWith(ret);
});

/**
* @param {FetchEvent} e
*/
async function fetchHandler(e) {
const req = e.request;
const urlStr = req.url;
const urlObj = new URL(urlStr);
let path = urlObj.searchParams.get('q');
if (path) {
return Response.redirect('https://' + urlObj.host + PREFIX + path, 301);
}

// 提取嵌套 URL
path = urlObj.href.substr(urlObj.origin.length + PREFIX.length).replace(/^https?:\/+/, 'https://');

if (checkUrl(urlStr)) {
return httpHandler(req, path);
} else {
try {
return await fetch(ASSET_URL + path);
} catch (err) {
return makeRes('Asset fetch error:\n' + err.message, 502);
}
}
}

/**
* @param {Request} req
* @param {string} pathname
*/
async function httpHandler(req, pathname) {
const reqHdrRaw = req.headers;

// 处理预检请求
if (req.method === 'OPTIONS' && reqHdrRaw.has('access-control-request-headers')) {
return new Response(null, PREFLIGHT_INIT);
}

const reqHdrNew = new Headers(reqHdrRaw);

let urlStr = pathname;
let flag = !whiteList.length;
for (let i of whiteList) {
if (urlStr.includes(i)) {
flag = true;
break;
}
}
if (!flag) {
return makeRes('Blocked by whitelist', 403);
}

if (urlStr.search(/^https?:\/\//) !== 0) {
urlStr = 'https://' + urlStr;
}
const urlObj = newUrl(urlStr);
if (!urlObj) {
return makeRes('Invalid URL: ' + urlStr, 400);
}

/** @type {RequestInit} */
const reqInit = {
method: req.method,
headers: reqHdrNew,
redirect: 'manual',
body: req.body
};
return proxy(urlObj, reqInit);
}

/**
* @param {URL} urlObj
* @param {RequestInit} reqInit
*/
async function proxy(urlObj, reqInit) {
try {
// 添加超时控制,防止长时间等待
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时
reqInit.signal = controller.signal;

const res = await fetch(urlObj.href, reqInit);
clearTimeout(timeoutId);

const resHdrOld = res.headers;
const resHdrNew = new Headers(resHdrOld);
const status = res.status;

// 处理重定向
if (resHdrNew.has('location')) {
let _location = resHdrNew.get('location');
if (checkUrl(_location)) {
resHdrNew.set('location', PREFIX + _location);
} else {
reqInit.redirect = 'follow';
return proxy(newUrl(_location), reqInit);
}
}

resHdrNew.set('access-control-expose-headers', '*');
resHdrNew.set('access-control-allow-origin', '*');

resHdrNew.delete('content-security-policy');
resHdrNew.delete('content-security-policy-report-only');
resHdrNew.delete('clear-site-data');

return new Response(res.body, {
status,
headers: resHdrNew,
});
} catch (err) {
return makeRes('Proxy error: ' + err.message, 502);
}
}