diff --git a/README.md b/README.md index 19fb65d..e2b5117 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ github release、archive以及项目文件的加速项目,支持clone,有Clo - python版本支持进行文件大小限制,超过设定返回原地址 [issue #8](https://github.com/hunshcn/gh-proxy/issues/8) +- python版本支持特定user/repo 封禁/白名单 以及passby [issue #41](https://github.com/hunshcn/gh-proxy/issues/41) + ## 使用 直接在copy出来的url前加`https://gh.api.99988866.xyz/`即可 diff --git a/app/main.py b/app/main.py index 9bab57c..ec74fc1 100644 --- a/app/main.py +++ b/app/main.py @@ -16,19 +16,36 @@ jsdelivr = 0 cnpmjs = 0 size_limit = 1024 * 1024 * 1024 * 999 # 允许的文件大小,默认999GB,相当于无限制了 https://github.com/hunshcn/gh-proxy/issues/8 + +""" + 先生效白名单再匹配黑名单,pass_list匹配到的会直接302到jsdelivr/cnpmjs而忽略设置 + 每个规则一行,可以封禁某个用户的所有仓库,也可以封禁某个用户的特定仓库,下方用黑名单示例,白名单同理 + user1 # 封禁user1的所有仓库 + user1/repo1 # 封禁user1的repo1 + */repo1 # 封禁所有叫做repo1的仓库 +""" +white_list = ''' +''' +back_list = ''' +''' +pass_list = ''' +''' + HOST = '127.0.0.1' # 监听地址,建议监听本地然后由web服务器反代 PORT = 80 # 监听端口 ASSET_URL = 'https://hunshcn.github.io/gh-proxy' # 主页 +white_list = [tuple([x.replace(' ', '') for x in i.split('/')]) for i in white_list.split('\n') if i] +back_list = [tuple([x.replace(' ', '') for x in i.split('/')]) for i in back_list.split('\n') if i] app = Flask(__name__) CHUNK_SIZE = 1024 * 10 index_html = requests.get(ASSET_URL, timeout=10).text icon_r = requests.get(ASSET_URL + '/favicon.ico', timeout=10).content -exp1 = re.compile(r'^(?:https?://)?github\.com/.+?/.+?/(?:releases|archive)/.*$') -exp2 = re.compile(r'^(?:https?://)?github\.com/.+?/.+?/(?:blob)/.*$') -exp3 = re.compile(r'^(?:https?://)?github\.com/.+?/.+?/(?:info|git-).*$') -exp4 = re.compile(r'^(?:https?://)?raw\.githubusercontent\.com/.+?/.+?/.+?/.+$') -exp5 = re.compile(r'^(?:https?://)?gist\.(?:githubusercontent|github)\.com/.+?/.+?/.+$') +exp1 = re.compile(r'^(?:https?://)?github\.com/(?P.+?)/(?P.+?)/(?:releases|archive)/.*$') +exp2 = re.compile(r'^(?:https?://)?github\.com/(?P.+?)/(?P.+?)/(?:blob|raw)/.*$') +exp3 = re.compile(r'^(?:https?://)?github\.com/(?P.+?)/(?P.+?)/(?:info|git-).*$') +exp4 = re.compile(r'^(?:https?://)?raw\.(?:githubusercontent|github)\.com/(?P.+?)/(?P.+?)/.+?/.+$') +exp5 = re.compile(r'^(?:https?://)?gist\.(?:githubusercontent|github)\.com/(?P.+?)/.+?/.+$') requests.sessions.default_headers = lambda: CaseInsensitiveDict() @@ -92,15 +109,35 @@ def proxy(u): u = u if u.startswith('http') else 'https://' + u if u.rfind('://', 3, 9) == -1: u = u.replace('s:/', 's://', 1) # uwsgi会将//传递为/ - if not any([i.match(u) for i in [exp1, exp2, exp3, exp4, exp5]]): + pass_by = False + for exp in (exp1, exp2, exp3, exp4, exp5): + m = exp.match(u) + if m: + m = tuple(m.groups()) + if white_list: + for i in white_list: + if m[:len(i)] == i or i[0] == '*' and len(m) == 2 and m[1] == i[1]: + break + else: + return Response('Forbidden by white list.', status=403) + for i in back_list: + if m[:len(i)] == i or i[0] == '*' and len(m) == 2 and m[1] == i[1]: + return Response('Forbidden by black list.', status=403) + for i in pass_list: + if m[:len(i)] == i or i[0] == '*' and len(m) == 2 and m[1] == i[1]: + pass_by = True + break + break + else: return Response('Invalid input.', status=403) - if jsdelivr and exp2.match(u): + + if (jsdelivr or pass_by) and exp2.match(u): u = u.replace('/blob/', '@', 1).replace('github.com', 'cdn.jsdelivr.net/gh', 1) return redirect(u) - elif cnpmjs and exp3.match(u): + elif (cnpmjs or pass_by) and exp3.match(u): u = u.replace('github.com', 'github.com.cnpmjs.org', 1) + request.url.replace(request.base_url, '', 1) return redirect(u) - elif jsdelivr and exp4.match(u): + elif (jsdelivr or pass_by) and exp4.match(u): u = re.sub(r'(\.com/.*?/.+?)/(.+?/)', r'\1@\2', u, 1) u = u.replace('raw.githubusercontent.com', 'cdn.jsdelivr.net/gh', 1) return redirect(u) @@ -133,5 +170,6 @@ def generate(): # return Response('Illegal input', status=403, mimetype='text/html; charset=UTF-8') +app.debug = True if __name__ == '__main__': app.run(host=HOST, port=PORT)