minor api-key handling refactor

This commit is contained in:
Nicolás Sánchez 2025-08-19 20:12:26 -03:00
parent 9ed2927c0b
commit b383cde1fe
1 changed files with 255 additions and 271 deletions

120
main.py
View File

@ -8,6 +8,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
from threading import Thread from threading import Thread
from flask import Flask, jsonify, request from flask import Flask, jsonify, request
from waitress import serve from waitress import serve
from functools import wraps
import time import time
import datetime import datetime
import json import json
@ -18,7 +19,7 @@ import os
def load_keys_from_db(file_name: str) -> list: def load_keys_from_db(file_name: str) -> list:
''' '''
Load valid API keys Load valid API keys to a set
Parameters Parameters
---------- ----------
@ -37,7 +38,7 @@ def load_keys_from_db(file_name: str) -> list:
data = database_cursor.fetchall() data = database_cursor.fetchall()
database_connection.close() database_connection.close()
valid_keys = [line[1] for line in data] valid_keys = {line[1] for line in data}
return valid_keys return valid_keys
@ -126,8 +127,21 @@ def main():
earn_api = Flask(__name__) earn_api = Flask(__name__)
#Helper functions
def require_api_key(func):
'''
Validates API key
'''
@wraps(func)
def wrapper(*args, **kwargs):
key = request.headers.get("X-API-KEY")
if not key or key not in valid_keys:
return jsonify({'Error': 'API key not valid'}), 401
return func(*args, **kwargs)
return wrapper
@earn_api.route("/get_global_status", methods=['GET']) @earn_api.route("/get_global_status", methods=['GET'])
@require_api_key
def get_global_status(): def get_global_status():
''' '''
GET request GET request
@ -136,7 +150,6 @@ def get_global_status():
None None
''' '''
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
response = {} response = {}
for item in earners: for item in earners:
response[str(item.connector)] = {"currency": item.get_currency(), response[str(item.connector)] = {"currency": item.get_currency(),
@ -152,10 +165,10 @@ def get_global_status():
"last_redemption": item.get_last_redemption()} "last_redemption": item.get_last_redemption()}
response["uptime"] = time.time() - start_time response["uptime"] = time.time() - start_time
return jsonify(response) return jsonify(response)
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/toggle_pause", methods=['POST']) @earn_api.route("/toggle_pause", methods=['POST'])
@require_api_key
def toggle_pause(): def toggle_pause():
''' '''
GET request GET request
@ -164,24 +177,22 @@ def toggle_pause():
broker: str broker: str
''' '''
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
if request.json is None: if request.json is None:
return jsonify({'Error': 'request.json is None'}) return jsonify({'Error': 'request.json is None'})
broker = request.json["broker"] broker = request.json["broker"]
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
for item in earners: for item in earners:
if str(item.connector)==broker: if str(item.connector)==broker:
item.toggle_pause() item.toggle_pause()
return jsonify({'Status': item.get_is_paused()}) return jsonify({'Status': item.get_is_paused()})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/get_step_size", methods=["GET"]) @earn_api.route("/get_step_size", methods=["GET"])
@require_api_key
def get_step_size(): def get_step_size():
''' '''
GET request GET request
@ -190,31 +201,27 @@ def get_step_size():
broker: str broker: str
''' '''
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
broker = request.args.get("broker") broker = request.args.get("broker")
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
for item in earners: for item in earners:
if str(item.connector)==broker: if str(item.connector)==broker:
return jsonify({'step_size': item.get_step_size()}) return jsonify({'step_size': item.get_step_size()})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/set_step_size", methods=["POST"]) @earn_api.route("/set_step_size", methods=["POST"])
@require_api_key
def set_step_size(): def set_step_size():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
if request.json is None: if request.json is None:
return jsonify({'Error': 'request.json is None'}) return jsonify({'Error': 'request.json is None'})
broker = request.json["broker"] broker = request.json["broker"]
new_step_size = request.json["new_step_size"] new_step_size = request.json["new_step_size"]
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
if new_step_size is None: if new_step_size is None:
return jsonify({'Error': 'new_step_size is None'}) return jsonify({'Error': 'new_step_size is None'})
@ -223,36 +230,32 @@ def set_step_size():
item.set_step_size(new_step_size) item.set_step_size(new_step_size)
return jsonify({'step_size': new_step_size}) return jsonify({'step_size': new_step_size})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/get_percentage", methods=["GET"]) @earn_api.route("/get_percentage", methods=["GET"])
@require_api_key
def get_percentage(): def get_percentage():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
broker = request.args.get("broker") broker = request.args.get("broker")
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
for item in earners: for item in earners:
if str(item.connector)==broker: if str(item.connector)==broker:
return jsonify({'percentage': item.get_percentage()}) return jsonify({'percentage': item.get_percentage()})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/set_percentage", methods=["POST"]) @earn_api.route("/set_percentage", methods=["POST"])
@require_api_key
def set_percentage(): def set_percentage():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
if request.json is None: if request.json is None:
return jsonify({'Error': 'request.json is None'}) return jsonify({'Error': 'request.json is None'})
broker = request.json["broker"] broker = request.json["broker"]
new_percentage = request.json["new_percentage"] new_percentage = request.json["new_percentage"]
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
if new_percentage is None: if new_percentage is None:
return jsonify({'Error': 'new_step_size is None'}) return jsonify({'Error': 'new_step_size is None'})
@ -261,36 +264,32 @@ def set_percentage():
item.set_percentage(new_percentage) item.set_percentage(new_percentage)
return jsonify({'percentage': new_percentage}) return jsonify({'percentage': new_percentage})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/get_time_between_subscriptions", methods=["GET"]) @earn_api.route("/get_time_between_subscriptions", methods=["GET"])
@require_api_key
def get_time_between_subscriptions(): def get_time_between_subscriptions():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
broker = request.args.get("broker") broker = request.args.get("broker")
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
for item in earners: for item in earners:
if str(item.connector)==broker: if str(item.connector)==broker:
return jsonify({'time_between_subscriptions': item.get_time_between_subscriptions()}) return jsonify({'time_between_subscriptions': item.get_time_between_subscriptions()})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/set_time_between_subscriptions", methods=["POST"]) @earn_api.route("/set_time_between_subscriptions", methods=["POST"])
@require_api_key
def set_time_between_subscriptions(): def set_time_between_subscriptions():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
if request.json is None: if request.json is None:
return jsonify({'Error': 'request.json is None'}) return jsonify({'Error': 'request.json is None'})
broker = request.json["broker"] broker = request.json["broker"]
new_time_between_subscriptions = request.json["new_time_between_subscriptions"] new_time_between_subscriptions = request.json["new_time_between_subscriptions"]
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
if new_time_between_subscriptions is None: if new_time_between_subscriptions is None:
return jsonify({'Error': 'new_step_size is None'}) return jsonify({'Error': 'new_step_size is None'})
@ -299,36 +298,32 @@ def set_time_between_subscriptions():
item.set_time_between_subscriptions(new_time_between_subscriptions) item.set_time_between_subscriptions(new_time_between_subscriptions)
return jsonify({'time_between_subscriptions': new_time_between_subscriptions}) return jsonify({'time_between_subscriptions': new_time_between_subscriptions})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/get_time_between_redemptions", methods=["GET"]) @earn_api.route("/get_time_between_redemptions", methods=["GET"])
@require_api_key
def get_time_between_redemptions(): def get_time_between_redemptions():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
broker = request.args.get("broker") broker = request.args.get("broker")
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
for item in earners: for item in earners:
if str(item.connector)==broker: if str(item.connector)==broker:
return jsonify({'time_between_redemptions': item.get_time_between_redemptions()}) return jsonify({'time_between_redemptions': item.get_time_between_redemptions()})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/set_time_between_redemptions", methods=["POST"]) @earn_api.route("/set_time_between_redemptions", methods=["POST"])
@require_api_key
def set_time_between_redemptions(): def set_time_between_redemptions():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
if request.json is None: if request.json is None:
return jsonify({'Error': 'request.json is None'}) return jsonify({'Error': 'request.json is None'})
broker = request.json["broker"] broker = request.json["broker"]
new_time_between_redemptions = request.json["new_time_between_redemptions"] new_time_between_redemptions = request.json["new_time_between_redemptions"]
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
if new_time_between_redemptions is None: if new_time_between_redemptions is None:
return jsonify({'Error': 'new_step_size is None'}) return jsonify({'Error': 'new_step_size is None'})
@ -337,34 +332,30 @@ def set_time_between_redemptions():
item.set_time_between_redemptions(new_time_between_redemptions) item.set_time_between_redemptions(new_time_between_redemptions)
return jsonify({'time_between_redemptions': new_time_between_redemptions}) return jsonify({'time_between_redemptions': new_time_between_redemptions})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/get_minimum_amount_in_trading_account", methods=["GET"]) @earn_api.route("/get_minimum_amount_in_trading_account", methods=["GET"])
@require_api_key
def get_minimum_amount_in_trading_account(): def get_minimum_amount_in_trading_account():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
broker = request.args.get("broker") broker = request.args.get("broker")
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
for item in earners: for item in earners:
if str(item.connector)==broker: if str(item.connector)==broker:
return jsonify({'minimum_amount_in_trading_account': item.get_minimum_amount_in_trading_account()}) return jsonify({'minimum_amount_in_trading_account': item.get_minimum_amount_in_trading_account()})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/set_minimum_amount_in_trading_account", methods=["POST"]) @earn_api.route("/set_minimum_amount_in_trading_account", methods=["POST"])
@require_api_key
def set_minimum_amount_in_trading_account(): def set_minimum_amount_in_trading_account():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
broker = request.json["broker"] broker = request.json["broker"]
new_minimum_amount_in_trading_account = request.json["new_minimum_amount_in_trading_account"] new_minimum_amount_in_trading_account = request.json["new_minimum_amount_in_trading_account"]
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
if new_minimum_amount_in_trading_account is None: if new_minimum_amount_in_trading_account is None:
return jsonify({'Error': 'new_step_size is None'}) return jsonify({'Error': 'new_step_size is None'})
@ -373,58 +364,52 @@ def set_minimum_amount_in_trading_account():
item.set_minimum_amount_in_trading_account(new_minimum_amount_in_trading_account) item.set_minimum_amount_in_trading_account(new_minimum_amount_in_trading_account)
return jsonify({'minimum_amount_in_trading_account': new_minimum_amount_in_trading_account}) return jsonify({'minimum_amount_in_trading_account': new_minimum_amount_in_trading_account})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/get_last_subscription", methods=["GET"]) @earn_api.route("/get_last_subscription", methods=["GET"])
@require_api_key
def get_last_subscription(): def get_last_subscription():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
broker = request.args.get("broker") broker = request.args.get("broker")
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
for item in earners: for item in earners:
if str(item.connector)==broker: if str(item.connector)==broker:
return jsonify({'last_subscription': item.get_last_subscription()}) return jsonify({'last_subscription': item.get_last_subscription()})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/get_last_redemption", methods=["GET"]) @earn_api.route("/get_last_redemption", methods=["GET"])
@require_api_key
def get_last_redemption(): def get_last_redemption():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
broker = request.args.get("broker") broker = request.args.get("broker")
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
for item in earners: for item in earners:
if str(item.connector)==broker: if str(item.connector)==broker:
return jsonify({'last_redemption': item.get_last_redemption()}) return jsonify({'last_redemption': item.get_last_redemption()})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/get_total_balance", methods=["GET"]) @earn_api.route("/get_total_balance", methods=["GET"])
@require_api_key
def get_total_balance(): def get_total_balance():
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
broker = request.args.get("broker") broker = request.args.get("broker")
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
for item in earners: for item in earners:
if str(item.connector)==broker: if str(item.connector)==broker:
return jsonify({'trading_balance': item.get_trading_balance(), 'earning_balance': item.get_earning_balance()}) return jsonify({'trading_balance': item.get_trading_balance(), 'earning_balance': item.get_earning_balance()})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/subscribe", methods=["POST"]) @earn_api.route("/subscribe", methods=["POST"])
@require_api_key
def subscribe(): def subscribe():
''' '''
args: args:
@ -432,8 +417,6 @@ def subscribe():
amount: amount to subscribe amount: amount to subscribe
force_pause: True or False force_pause: True or False
''' '''
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
if request.json is None: if request.json is None:
return jsonify({'Error': 'request.json is None'}) return jsonify({'Error': 'request.json is None'})
broker = request.json["broker"] broker = request.json["broker"]
@ -443,7 +426,7 @@ def subscribe():
force_pause = request.json["force_pause"] force_pause = request.json["force_pause"]
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
if amount is None: if amount is None:
return jsonify({'Error': 'amount is None'}) return jsonify({'Error': 'amount is None'})
@ -454,10 +437,10 @@ def subscribe():
subscription = item.subscribe(amount, force_pause=force_pause) subscription = item.subscribe(amount, force_pause=force_pause)
return jsonify({'Success': str(subscription)}) return jsonify({'Success': str(subscription)})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
@earn_api.route("/redeem", methods=["POST"]) @earn_api.route("/redeem", methods=["POST"])
@require_api_key
def redeem(): def redeem():
''' '''
args: args:
@ -465,8 +448,6 @@ def redeem():
amount: amount to redeem amount: amount to redeem
force_pause: True or False force_pause: True or False
''' '''
if "X-API-KEY" in request.headers and request.headers.get("X-API-KEY") in valid_keys:
valid_brokers = [str(item.connector) for item in earners]
if request.json is None: if request.json is None:
return jsonify({'Error': 'request.json is None'}) return jsonify({'Error': 'request.json is None'})
broker = request.json["broker"] broker = request.json["broker"]
@ -476,7 +457,7 @@ def redeem():
force_pause = request.json["force_pause"] force_pause = request.json["force_pause"]
if broker is None: if broker is None:
return jsonify({'Error': 'broker is None'}) return jsonify({'Error': 'broker is None'})
if broker not in valid_brokers: if broker not in valid_broker_list:
return jsonify({'Error': 'broker not valid'}) return jsonify({'Error': 'broker not valid'})
if amount is None: if amount is None:
return jsonify({'Error': 'amount is None'}) return jsonify({'Error': 'amount is None'})
@ -487,7 +468,7 @@ def redeem():
redemption = item.redeem(amount, force_pause=force_pause) redemption = item.redeem(amount, force_pause=force_pause)
return jsonify({'Success': str(redemption)}) return jsonify({'Success': str(redemption)})
return jsonify({'Error': 'broker not found'}) return jsonify({'Error': 'broker not found'})
return jsonify({'Error': 'API key not valid'}), 401
def run_API(port): def run_API(port):
@ -525,6 +506,9 @@ if __name__=="__main__":
for item in config["exchanges"]: for item in config["exchanges"]:
earners.append(earner(connectors[item], config["exchanges"][item])) earners.append(earner(connectors[item], config["exchanges"][item]))
#Valid broker list
valid_broker_list = [str(item.connector) for item in earners]
#Load valid API keys #Load valid API keys
valid_keys = load_keys_from_db("keys/api_credentials.db") valid_keys = load_keys_from_db("keys/api_credentials.db")