-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
236 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
from flask_caching import Cache | ||
|
||
cache = Cache() | ||
|
||
|
||
def init_cache_app(app): | ||
cache.init_app(app, config={ | ||
'CACHE_TYPE': app.config.get('CACHE_TYPE', 'SimpleCache'), | ||
'CACHE_DEFAULT_TIMEOUT': app.config.get('CACHE_DEFAULT_TIMEOUT', 300), | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,5 +21,7 @@ | |
} | ||
} | ||
} | ||
} | ||
}, | ||
"CACHE_TYPE": "SimpleCache", | ||
"CACHE_DEFAULT_TIMEOUT": 300 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
from flask_smorest import Blueprint | ||
from flask import jsonify | ||
|
||
bp = Blueprint("errors", __name__) | ||
|
||
|
||
@bp.app_errorhandler(400) | ||
def bad_request(error): | ||
return jsonify({ | ||
"status": { | ||
"code": 400, | ||
"message": "Client side error!" | ||
}, | ||
"data": None, | ||
}), 400 | ||
|
||
|
||
@bp.app_errorhandler(404) | ||
def not_found(error): | ||
return jsonify({ | ||
"status": { | ||
"code": 404, | ||
"message": "URL not found!" | ||
}, | ||
"data": None, | ||
}), 404 | ||
|
||
|
||
@bp.app_errorhandler(405) | ||
def method_not_allowed(error): | ||
return jsonify({ | ||
"status": { | ||
"code": 405, | ||
"message": "Request method not allowed!" | ||
}, | ||
"data": None, | ||
}), 405 | ||
|
||
|
||
@bp.app_errorhandler(429) | ||
def rate_limit_exceeded(error): | ||
return jsonify({ | ||
"status": { | ||
"code": 429, | ||
"message": "Rate limit exceeded. Please try again later." | ||
}, | ||
"data": None | ||
}), 429 | ||
|
||
|
||
@bp.app_errorhandler(500) | ||
def internal_server_error(error): | ||
return jsonify({ | ||
"status": { | ||
"code": 500, | ||
"message": "Server error!" | ||
}, | ||
"data": None, | ||
}), 500 |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
from flask_smorest import Blueprint | ||
from flask import jsonify, request | ||
from auth import auth | ||
from cache import cache | ||
from rate_limiter import limiter | ||
|
||
|
||
bp = Blueprint("index", | ||
"items", | ||
description="Operations on ML model endpoint") | ||
|
||
|
||
@bp.route("/") | ||
@limiter.limit("10 per day") | ||
@cache.cached(timeout=60) | ||
def index(): | ||
return jsonify({ | ||
"status": { | ||
"code": 200, | ||
"message": "Success fetching the API!" | ||
}, | ||
"data": None | ||
}), 200 | ||
|
||
|
||
@bp.route("/post", methods=["POST"]) | ||
@auth.login_required() | ||
def post(): | ||
if request.method == "POST": | ||
input_data = request.get_json() | ||
return jsonify(input_data), 200 | ||
else: | ||
return jsonify({ | ||
"status": { | ||
"code": 405, | ||
"message": "Invalid request method", | ||
}, | ||
"data": None, | ||
}), 405 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from flask import jsonify | ||
from flask_limiter import Limiter | ||
from flask_limiter.util import get_remote_address | ||
|
||
limiter = Limiter( | ||
key_func=get_remote_address, | ||
default_limits=["200 per day", "50 per hour"], | ||
storage_uri="memory://", | ||
) | ||
|
||
|
||
def init_rate_limiter(app): | ||
limiter.init_app(app) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,29 @@ | ||
apispec==6.3.0 | ||
click==8.1.3 | ||
colorama==0.4.6 | ||
Flask==2.2.3 | ||
Flask-Cors==3.0.10 | ||
Flask-HTTPAuth==4.7.0 | ||
flask-smorest==0.41.0 | ||
gunicorn==20.1.0 | ||
importlib-metadata==6.5.0 | ||
apispec==6.4.0 | ||
blinker==1.7.0 | ||
cachelib==0.9.0 | ||
click==8.1.7 | ||
Deprecated==1.2.14 | ||
Flask==3.0.2 | ||
Flask-Caching==2.1.0 | ||
Flask-Limiter==3.5.1 | ||
flask-smorest==0.43.0 | ||
gunicorn==21.2.0 | ||
importlib-metadata==7.0.1 | ||
importlib-resources==6.1.1 | ||
itsdangerous==2.1.2 | ||
Jinja2==3.1.2 | ||
MarkupSafe==2.1.2 | ||
marshmallow==3.19.0 | ||
packaging==23.1 | ||
python-dotenv==1.0.0 | ||
six==1.16.0 | ||
webargs==8.2.0 | ||
Werkzeug==2.2.3 | ||
zipp==3.15.0 | ||
Jinja2==3.1.3 | ||
limits==3.8.0 | ||
markdown-it-py==3.0.0 | ||
MarkupSafe==2.1.5 | ||
marshmallow==3.20.2 | ||
mdurl==0.1.2 | ||
ordered-set==4.1.0 | ||
packaging==23.2 | ||
Pygments==2.17.2 | ||
PyJWT==2.8.0 | ||
rich==13.7.0 | ||
typing_extensions==4.9.0 | ||
webargs==8.4.0 | ||
Werkzeug==3.0.1 | ||
wrapt==1.16.0 | ||
zipp==3.17.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
from flask import jsonify, request | ||
from functools import wraps | ||
import jwt | ||
from config import CONFIG | ||
|
||
admin = False | ||
SECRET_KEY = CONFIG['SECRET_KEY'] | ||
|
||
|
||
def admin_require(f): | ||
@wraps(f) | ||
def decorated_function(*args, **kwargs): | ||
if admin: | ||
return f(*args, **kwargs) | ||
else: | ||
return jsonify({ | ||
"status": { | ||
"code": 403, | ||
"message": "Forbidden", | ||
}, | ||
"data": None | ||
}), 403 | ||
|
||
return decorated_function | ||
|
||
|
||
def token_required(f): | ||
@wraps(f) | ||
def decorator(*args, **kwargs): | ||
token = request.headers.get('Authorization', None) | ||
if not token: | ||
return jsonify({ | ||
"status": { | ||
"code": 401, | ||
"message": "Invalid token", | ||
}, | ||
"data": None | ||
}), 401 | ||
try: | ||
token_prefix, token_value = token.split() | ||
if token_prefix.lower() != 'bearer': | ||
raise ValueError('Invalid token prefix') | ||
data = jwt.decode(token_value, SECRET_KEY, algorithms=['HS256']) | ||
except jwt.ExpiredSignatureError: | ||
return jsonify({ | ||
"status": { | ||
"code": 401, | ||
"message": "Token has expired", | ||
}, | ||
"data": None | ||
}), 401 | ||
except jwt.InvalidTokenError: | ||
return jsonify({ | ||
"status": { | ||
"code": 401, | ||
"message": "Invalid token" | ||
}, | ||
"data": None, | ||
}), 401 | ||
except ValueError: | ||
return jsonify({ | ||
"status": { | ||
"code": 401, | ||
"message": "Invalid token format", | ||
}, | ||
"data": None | ||
}), 401 | ||
return f(data, *args, **kwargs) | ||
return decorator |