chg: Move API to restx
parent
80b5c045ac
commit
ec56911ba4
|
@ -120,5 +120,9 @@ rawdata
|
||||||
|
|
||||||
storage/db/
|
storage/db/
|
||||||
storage/kvrocks*
|
storage/kvrocks*
|
||||||
website/web/static/d3.v5.js
|
ranking/db/
|
||||||
|
ranking/kvrocks*
|
||||||
|
website/web/static/d3.*.js
|
||||||
website/web/static/bootstrap-select.min.*
|
website/web/static/bootstrap-select.min.*
|
||||||
|
|
||||||
|
*.pid
|
||||||
|
|
|
@ -1,27 +1,21 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import json
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
from urllib.parse import urljoin
|
from typing import Dict, Any, Tuple, List, Optional
|
||||||
from typing import Dict, Any, Tuple, List, Optional, Union
|
|
||||||
|
|
||||||
import pycountry # type: ignore
|
from flask import Flask, render_template, request, session, redirect, url_for
|
||||||
import requests
|
|
||||||
|
|
||||||
from flask import Flask, render_template, request, session, Response, redirect, url_for
|
|
||||||
from flask_bootstrap import Bootstrap # type: ignore
|
from flask_bootstrap import Bootstrap # type: ignore
|
||||||
from flask_restx import Api # type: ignore
|
from flask_restx import Api # type: ignore
|
||||||
|
|
||||||
from bgpranking.bgpranking import BGPRanking
|
from bgpranking.bgpranking import BGPRanking
|
||||||
from bgpranking.default import get_config
|
|
||||||
from bgpranking.helpers import get_ipasn
|
from bgpranking.helpers import get_ipasn
|
||||||
|
|
||||||
from .genericapi import api as generic_api
|
from .genericapi import api as generic_api
|
||||||
from .helpers import get_secret_key
|
from .helpers import get_secret_key, load_session, get_country_codes
|
||||||
from .proxied import ReverseProxied
|
from .proxied import ReverseProxied
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
@ -36,41 +30,6 @@ app.config['BOOTSTRAP_SERVE_LOCAL'] = True
|
||||||
bgpranking = BGPRanking()
|
bgpranking = BGPRanking()
|
||||||
|
|
||||||
|
|
||||||
# ############# Helpers #############
|
|
||||||
|
|
||||||
def load_session():
|
|
||||||
if request.method == 'POST':
|
|
||||||
d = request.form
|
|
||||||
elif request.method == 'GET':
|
|
||||||
d = request.args # type: ignore
|
|
||||||
|
|
||||||
for key in d:
|
|
||||||
if '_all' in d.getlist(key):
|
|
||||||
session.pop(key, None)
|
|
||||||
else:
|
|
||||||
values = [v for v in d.getlist(key) if v]
|
|
||||||
if values:
|
|
||||||
if len(values) == 1:
|
|
||||||
session[key] = values[0]
|
|
||||||
else:
|
|
||||||
session[key] = values
|
|
||||||
|
|
||||||
# Edge cases
|
|
||||||
if 'asn' in session:
|
|
||||||
session.pop('country', None)
|
|
||||||
elif 'country' in session:
|
|
||||||
session.pop('asn', None)
|
|
||||||
if 'date' not in session:
|
|
||||||
session['date'] = (date.today() - timedelta(days=1)).isoformat()
|
|
||||||
|
|
||||||
|
|
||||||
def get_country_codes():
|
|
||||||
for c in pycountry.countries:
|
|
||||||
yield c.alpha_2, c.name
|
|
||||||
|
|
||||||
# ############# Helpers ######################
|
|
||||||
|
|
||||||
|
|
||||||
# ############# Web UI #############
|
# ############# Web UI #############
|
||||||
|
|
||||||
@app.route('/', methods=['GET', 'POST'])
|
@app.route('/', methods=['GET', 'POST'])
|
||||||
|
@ -176,109 +135,11 @@ def ipasn():
|
||||||
|
|
||||||
# ############# Web UI #############
|
# ############# Web UI #############
|
||||||
|
|
||||||
|
|
||||||
# ############# Json outputs #############
|
|
||||||
|
|
||||||
@app.route('/ipasn_history/', defaults={'path': ''}, methods=['GET', 'POST'])
|
|
||||||
@app.route('/ipasn_history/<path:path>', methods=['GET', 'POST'])
|
|
||||||
def ipasn_history_proxy(path):
|
|
||||||
|
|
||||||
path_for_ipasnhistory = request.full_path.replace('/ipasn_history', '')
|
|
||||||
if '/?' in path_for_ipasnhistory:
|
|
||||||
path_for_ipasnhistory = path_for_ipasnhistory.replace('/?', '/ip?')
|
|
||||||
proxied_url = urljoin(get_config('generic', 'ipasnhistory_url'), path_for_ipasnhistory)
|
|
||||||
if request.method in ['GET', 'HEAD']:
|
|
||||||
to_return = requests.get(proxied_url).json()
|
|
||||||
elif request.method == 'POST':
|
|
||||||
to_return = requests.post(proxied_url, data=request.data).json()
|
|
||||||
return Response(json.dumps(to_return), mimetype='application/json')
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/json/asn', methods=['POST'])
|
|
||||||
def json_asn():
|
|
||||||
# TODO
|
|
||||||
# * Filter on date => if only returning one descr, return the desription at that date
|
|
||||||
query: Dict[str, Any] = request.get_json(force=True) # type: ignore
|
|
||||||
to_return: Dict[str, Union[str, Dict[str, Any]]] = {'meta': query, 'response': {}}
|
|
||||||
if 'asn' not in query:
|
|
||||||
to_return['error'] = f'You need to pass an asn - {query}'
|
|
||||||
return Response(json.dumps(to_return), mimetype='application/json')
|
|
||||||
|
|
||||||
asn_description_query = {'asn': query['asn']}
|
|
||||||
if 'all_descriptions' in query:
|
|
||||||
asn_description_query['all_descriptions'] = query['all_descriptions']
|
|
||||||
responses = bgpranking.get_asn_descriptions(**asn_description_query)['response']
|
|
||||||
to_return['response']['asn_description'] = responses # type: ignore
|
|
||||||
|
|
||||||
asn_rank_query = {'asn': query['asn']}
|
|
||||||
if 'date' in query:
|
|
||||||
asn_rank_query['date'] = query['date']
|
|
||||||
if 'source' in query:
|
|
||||||
asn_rank_query['source'] = query['source']
|
|
||||||
else:
|
|
||||||
asn_rank_query['with_position'] = True
|
|
||||||
if 'ipversion' in query:
|
|
||||||
asn_rank_query['ipversion'] = query['ipversion']
|
|
||||||
|
|
||||||
to_return['response']['ranking'] = bgpranking.asn_rank(**asn_rank_query)['response'] # type: ignore
|
|
||||||
return Response(json.dumps(to_return), mimetype='application/json')
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/json/asn_descriptions', methods=['POST'])
|
|
||||||
def asn_description():
|
|
||||||
query: Dict = request.get_json(force=True) # type: ignore
|
|
||||||
to_return: Dict[str, Union[str, Dict[str, Any]]] = {'meta': query, 'response': {}}
|
|
||||||
if 'asn' not in query:
|
|
||||||
to_return['error'] = f'You need to pass an asn - {query}'
|
|
||||||
return Response(json.dumps(to_return), mimetype='application/json')
|
|
||||||
|
|
||||||
to_return['response']['asn_descriptions'] = bgpranking.get_asn_descriptions(**query)['response'] # type: ignore
|
|
||||||
return Response(json.dumps(to_return), mimetype='application/json')
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/json/asn_history', methods=['GET', 'POST'])
|
|
||||||
def asn_history():
|
|
||||||
if request.method == 'GET':
|
|
||||||
load_session()
|
|
||||||
if 'asn' in session:
|
|
||||||
return Response(json.dumps(bgpranking.get_asn_history(**session)), mimetype='application/json')
|
|
||||||
|
|
||||||
query: Dict = request.get_json(force=True) # type: ignore
|
|
||||||
to_return: Dict[str, Union[str, Dict[str, Any]]] = {'meta': query, 'response': {}}
|
|
||||||
if 'asn' not in query:
|
|
||||||
to_return['error'] = f'You need to pass an asn - {query}'
|
|
||||||
return Response(json.dumps(to_return), mimetype='application/json')
|
|
||||||
|
|
||||||
to_return['response']['asn_history'] = bgpranking.get_asn_history(**query)['response'] # type: ignore
|
|
||||||
return Response(json.dumps(to_return), mimetype='application/json')
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/json/country_history', methods=['GET', 'POST'])
|
|
||||||
def country_history():
|
|
||||||
if request.method == 'GET':
|
|
||||||
load_session()
|
|
||||||
return Response(json.dumps(bgpranking.country_history(**session)), mimetype='application/json')
|
|
||||||
|
|
||||||
query: Dict = request.get_json(force=True) # type: ignore
|
|
||||||
to_return: Dict[str, Union[str, Dict[str, Any]]] = {'meta': query, 'response': {}}
|
|
||||||
to_return['response']['country_history'] = bgpranking.country_history(**query)['response'] # type: ignore
|
|
||||||
return Response(json.dumps(to_return), mimetype='application/json')
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/json/asns_global_ranking', methods=['POST'])
|
|
||||||
def json_asns_global_ranking():
|
|
||||||
query: Dict = request.get_json(force=True) # type: ignore
|
|
||||||
to_return: Dict[str, Union[str, Dict[str, Any]]] = {'meta': query, 'response': {}}
|
|
||||||
to_return['response'] = bgpranking.asns_global_ranking(**query)['response']
|
|
||||||
return Response(json.dumps(to_return), mimetype='application/json')
|
|
||||||
|
|
||||||
# ############# Json outputs #############
|
|
||||||
|
|
||||||
|
|
||||||
# Query API
|
# Query API
|
||||||
|
|
||||||
api = Api(app, title='BGP Ranking API',
|
api = Api(app, title='BGP Ranking API',
|
||||||
description='API to query BGP Ranking.',
|
description='API to query BGP Ranking.',
|
||||||
|
doc='/doc/',
|
||||||
version=pkg_resources.get_distribution('bgpranking').version)
|
version=pkg_resources.get_distribution('bgpranking').version)
|
||||||
|
|
||||||
api.add_namespace(generic_api)
|
api.add_namespace(generic_api)
|
||||||
|
|
|
@ -1,25 +1,20 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import pkg_resources
|
from typing import Dict, Any, Union
|
||||||
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
from flask import Flask
|
import requests
|
||||||
from flask_restx import Api, Resource # type: ignore
|
|
||||||
|
|
||||||
|
from flask import request, session
|
||||||
|
from flask_restx import Namespace, Resource, fields # type: ignore
|
||||||
|
|
||||||
|
from bgpranking.default import get_config
|
||||||
from bgpranking.bgpranking import BGPRanking
|
from bgpranking.bgpranking import BGPRanking
|
||||||
|
|
||||||
from .helpers import get_secret_key
|
from .helpers import load_session
|
||||||
from .proxied import ReverseProxied
|
|
||||||
|
|
||||||
app: Flask = Flask(__name__)
|
api = Namespace('BGP Ranking API', description='API to query BGP Ranking.', path='/')
|
||||||
|
|
||||||
app.wsgi_app = ReverseProxied(app.wsgi_app) # type: ignore
|
|
||||||
|
|
||||||
app.config['SECRET_KEY'] = get_secret_key()
|
|
||||||
|
|
||||||
api = Api(app, title='BGP Ranking API',
|
|
||||||
description='API to query BGP Ranking.',
|
|
||||||
version=pkg_resources.get_distribution('bgpranking').version)
|
|
||||||
|
|
||||||
bgpranking: BGPRanking = BGPRanking()
|
bgpranking: BGPRanking = BGPRanking()
|
||||||
|
|
||||||
|
@ -30,3 +25,149 @@ class RedisUp(Resource):
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
return bgpranking.check_redis_up()
|
return bgpranking.check_redis_up()
|
||||||
|
|
||||||
|
|
||||||
|
@api.route('/ipasn_history/')
|
||||||
|
@api.route('/ipasn_history/<path:path>')
|
||||||
|
class IPASNProxy(Resource):
|
||||||
|
|
||||||
|
def _proxy_url(self):
|
||||||
|
if request.full_path[-1] == '?':
|
||||||
|
full_path = request.full_path[:-1]
|
||||||
|
else:
|
||||||
|
full_path = request.full_path
|
||||||
|
path_for_ipasnhistory = full_path.replace('/ipasn_history', '')
|
||||||
|
if path_for_ipasnhistory.startswith('/?'):
|
||||||
|
path_for_ipasnhistory = path_for_ipasnhistory.replace('/?', '/ip?')
|
||||||
|
return urljoin(get_config('generic', 'ipasnhistory_url'), path_for_ipasnhistory)
|
||||||
|
|
||||||
|
def get(self, path=''):
|
||||||
|
url = self._proxy_url()
|
||||||
|
print(url)
|
||||||
|
return requests.get(url).json()
|
||||||
|
|
||||||
|
def post(self, path=''):
|
||||||
|
url = self._proxy_url()
|
||||||
|
return requests.post(url, data=request.data).json()
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Add other parameters for asn_rank
|
||||||
|
asn_query_fields = api.model('ASNQueryFields', {
|
||||||
|
'asn': fields.String(description='The Autonomus System Number to search', required=True)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@api.route('/json/asn')
|
||||||
|
class ASNRank(Resource):
|
||||||
|
|
||||||
|
@api.doc(body=asn_query_fields)
|
||||||
|
def post(self):
|
||||||
|
# TODO
|
||||||
|
# * Filter on date => if only returning one descr, return the desription at that date
|
||||||
|
query: Dict[str, Any] = request.get_json(force=True) # type: ignore
|
||||||
|
to_return: Dict[str, Union[str, Dict[str, Any]]] = {'meta': query, 'response': {}}
|
||||||
|
if 'asn' not in query:
|
||||||
|
to_return['error'] = f'You need to pass an asn - {query}'
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
asn_description_query = {'asn': query['asn']}
|
||||||
|
responses = bgpranking.get_asn_descriptions(**asn_description_query)['response']
|
||||||
|
to_return['response']['asn_description'] = responses # type: ignore
|
||||||
|
|
||||||
|
asn_rank_query = {'asn': query['asn']}
|
||||||
|
if 'date' in query:
|
||||||
|
asn_rank_query['date'] = query['date']
|
||||||
|
if 'source' in query:
|
||||||
|
asn_rank_query['source'] = query['source']
|
||||||
|
else:
|
||||||
|
asn_rank_query['with_position'] = True
|
||||||
|
if 'ipversion' in query:
|
||||||
|
asn_rank_query['ipversion'] = query['ipversion']
|
||||||
|
|
||||||
|
to_return['response']['ranking'] = bgpranking.asn_rank(**asn_rank_query)['response'] # type: ignore
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
asn_descr_fields = api.model('ASNDescriptionsFields', {
|
||||||
|
'asn': fields.String(description='The Autonomus System Number to search', required=True),
|
||||||
|
'all_descriptions': fields.Boolean(description='If true, returns all the descriptions instead of only the last one', default=False)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@api.route('/json/asn_descriptions')
|
||||||
|
class ASNDescription(Resource):
|
||||||
|
|
||||||
|
@api.doc(body=asn_descr_fields)
|
||||||
|
def post(self):
|
||||||
|
query: Dict = request.get_json(force=True) # type: ignore
|
||||||
|
to_return: Dict[str, Union[str, Dict[str, Any]]] = {'meta': query, 'response': {}}
|
||||||
|
if 'asn' not in query:
|
||||||
|
to_return['error'] = f'You need to pass an asn - {query}'
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
to_return['response']['asn_descriptions'] = bgpranking.get_asn_descriptions(**query)['response'] # type: ignore
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Add other parameters for get_asn_history
|
||||||
|
asn_history_fields = api.model('ASNQueryFields', {
|
||||||
|
'asn': fields.String(description='The Autonomus System Number to search', required=True)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@api.route('/json/asn_history')
|
||||||
|
class ASNHistory(Resource):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
load_session()
|
||||||
|
if 'asn' in session:
|
||||||
|
return bgpranking.get_asn_history(**session)
|
||||||
|
|
||||||
|
@api.doc(body=asn_history_fields)
|
||||||
|
def post(self):
|
||||||
|
query: Dict = request.get_json(force=True) # type: ignore
|
||||||
|
to_return: Dict[str, Union[str, Dict[str, Any]]] = {'meta': query, 'response': {}}
|
||||||
|
if 'asn' not in query:
|
||||||
|
to_return['error'] = f'You need to pass an asn - {query}'
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
to_return['response']['asn_history'] = bgpranking.get_asn_history(**query)['response'] # type: ignore
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Add other parameters for country_history
|
||||||
|
coutry_history_fields = api.model('CountryHistoryFields', {
|
||||||
|
'country': fields.String(description='The Country Code', required=True)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@api.route('/json/country_history')
|
||||||
|
class CountryHistory(Resource):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
load_session()
|
||||||
|
return bgpranking.country_history(**session)
|
||||||
|
|
||||||
|
@api.doc(body=coutry_history_fields)
|
||||||
|
def post(self):
|
||||||
|
query: Dict = request.get_json(force=True) # type: ignore
|
||||||
|
to_return: Dict[str, Union[str, Dict[str, Any]]] = {'meta': query, 'response': {}}
|
||||||
|
to_return['response']['country_history'] = bgpranking.country_history(**query)['response'] # type: ignore
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Add other parameters for asns_global_ranking
|
||||||
|
asns_global_ranking_fields = api.model('ASNsGlobalRankingFields', {
|
||||||
|
'date': fields.String(description='The date')
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@api.route('/json/asns_global_ranking')
|
||||||
|
class ASNsGlobalRanking(Resource):
|
||||||
|
|
||||||
|
@api.doc(body=asns_global_ranking_fields)
|
||||||
|
def post(self):
|
||||||
|
query: Dict = request.get_json(force=True) # type: ignore
|
||||||
|
to_return: Dict[str, Union[str, Dict[str, Any]]] = {'meta': query, 'response': {}}
|
||||||
|
to_return['response'] = bgpranking.asns_global_ranking(**query)['response']
|
||||||
|
return to_return
|
||||||
|
|
|
@ -2,9 +2,15 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from datetime import date, timedelta
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pycountry # type: ignore
|
||||||
|
|
||||||
|
from flask import request, session
|
||||||
|
|
||||||
from bgpranking.default import get_homedir
|
from bgpranking.default import get_homedir
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,3 +31,34 @@ def get_secret_key() -> bytes:
|
||||||
f.write(os.urandom(64))
|
f.write(os.urandom(64))
|
||||||
with secret_file_path.open('rb') as f:
|
with secret_file_path.open('rb') as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def load_session():
|
||||||
|
if request.method == 'POST':
|
||||||
|
d = request.form
|
||||||
|
elif request.method == 'GET':
|
||||||
|
d = request.args # type: ignore
|
||||||
|
|
||||||
|
for key in d:
|
||||||
|
if '_all' in d.getlist(key):
|
||||||
|
session.pop(key, None)
|
||||||
|
else:
|
||||||
|
values = [v for v in d.getlist(key) if v]
|
||||||
|
if values:
|
||||||
|
if len(values) == 1:
|
||||||
|
session[key] = values[0]
|
||||||
|
else:
|
||||||
|
session[key] = values
|
||||||
|
|
||||||
|
# Edge cases
|
||||||
|
if 'asn' in session:
|
||||||
|
session.pop('country', None)
|
||||||
|
elif 'country' in session:
|
||||||
|
session.pop('asn', None)
|
||||||
|
if 'date' not in session:
|
||||||
|
session['date'] = (date.today() - timedelta(days=1)).isoformat()
|
||||||
|
|
||||||
|
|
||||||
|
def get_country_codes():
|
||||||
|
for c in pycountry.countries:
|
||||||
|
yield c.alpha_2, c.name
|
||||||
|
|
Loading…
Reference in New Issue