From d4f7ab0b1a364b0b4b785c21538e0bf02cdc98b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20G=C3=BCrdo=C4=9Fan?= Date: Sun, 7 Jan 2024 21:51:18 +0300 Subject: [PATCH] Created a management panel for the DB, frontend w/ Bootstrap - Create an .env file like this: UPDATE_PASSWORD= - The admin (with password on the .env) can create, update records on /manage-records - Anyone can query records on /query-address. In here, the records can be updated by admin by giving the password. --- .gitignore | 4 + main.py | 215 ++++++++++++++++++++++++---------- templates/home.html | 57 +++++++++ templates/manage_records.html | 117 ++++++++++++++++++ templates/update_form.html | 154 ++++++++++++++++++++++++ 5 files changed, 484 insertions(+), 63 deletions(-) create mode 100644 .gitignore create mode 100644 templates/home.html create mode 100644 templates/manage_records.html create mode 100644 templates/update_form.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6cac23e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.env +*.env.example +*.env.local +*.env.staging \ No newline at end of file diff --git a/main.py b/main.py index acc78b5..dd61cd0 100644 --- a/main.py +++ b/main.py @@ -2,13 +2,14 @@ import os import flask -from flask import request, jsonify +from flask import request, jsonify, render_template from flask_cors import CORS import sqlite3 app = flask.Flask(__name__) CORS(app, resources={r"/api/*": {"origins": "*"}}) -#app.config["DEBUG"] = True +# app.config["DEBUG"] = True + def dict_factory(cursor, row): d = {} @@ -17,86 +18,172 @@ def dict_factory(cursor, row): return d -@app.route('/', methods=['GET']) +@app.route("/", methods=["GET"]) def home(): - return '''

Banano address API

-

Banano address API by Kirby. Work in progress

-/api/v1/resources/addresses?address=ban_31dhbgirwzd3ce7naor6o94woefws9hpxu4q8uxm1bz98w89zqpfks5rk3ad -
-/api/v1/resources/addresses?type=Distribution -
-/api/v1/resources/addresses?type=Exchange -
-/api/v1/resources/addresses?type=Gambling -
-/api/v1/resources/addresses/all -
-/api/v1/resources/addresses?illicit=1 -
-
-/api/v1/resources/intermediaries/all -
-/api/v1/resources/intermediaries?address=ban_113sf8z98qqihjis3m95gkb35z7ckfakbrtrmbtawf37hc6ixx7rtz53bq8o -
-/api/v1/resources/intermediaries?address=ban_113sf8z98qqihjis3m95gkb35z7ckfakbrtrmbtawf37hc6ixx7rtz53bq8o&address=ban_111c3xcromzadabqud7yer7ptzrocecsum9d9t8feoz81zdbu7gh63hnk7n4 -
-/api/v1/resources/intermediaries?service=ban_1gooj14qko1u6md87aga9c53nf4iphyt1ua7x3kq1wnkdh49u5mndqygbr1q -
-/api/v1/resources/intermediaries?service=ban_1gooj14qko1u6md87aga9c53nf4iphyt1ua7x3kq1wnkdh49u5mndqygbr1q&service=ban_1oaocnrcaystcdtaae6woh381wftyg4k7bespu19m5w18ze699refhyzu6bo -
-/api/v1/resources/intermediaries?service=ban_1oaocnrcaystcdtaae6woh381wftyg4k7bespu19m5w18ze699refhyzu6bo&address=ban_3fhhttfufikxikuxj5j6gndu5dokupa7j7t7dq5qkat19fm3mo84spe8krhz -
-/api/v1/resources/intermediaries/status -
-
-Source on Github -''' - - -@app.route('/api/v1/resources/addresses/all', methods=['GET']) + return render_template("home.html") + +@app.route("/api/v1/resources/addresses/all", methods=["GET"]) def known_all(): conn = sqlite3.connect(os.getcwd() + "/addresses.sqlite") conn.row_factory = dict_factory cur = conn.cursor() - all_addresses = cur.execute('SELECT * FROM addresses;').fetchall() + all_addresses = cur.execute("SELECT * FROM addresses;").fetchall() return jsonify(all_addresses) - @app.errorhandler(404) def page_not_found(e): return "

404

The resource could not be found.

", 404 -@app.route('/api/v1/resources/addresses', methods=['GET']) +@app.route("/query-address", methods=["GET"]) +def update_address_form(): + return render_template("update_form.html") + +@app.route("/manage-records", methods=["GET"]) +def manage_addresses(): + return render_template("manage_records.html") + +@app.route("/api/v1/resources/addresses/update", methods=["PUT"]) +def update_address(): + update_password = os.environ.get("UPDATE_PASSWORD", "default_password") + request_data = request.get_json() + + # Check password + if request_data.get("password") != update_password: + return jsonify({"error": "Unauthorized: Incorrect password"}), 401 + + address_id = request_data.get("id") + new_data = request_data.get("new_data") + + if not all([address_id, new_data]): + return jsonify({"error": "Missing data"}), 400 + + conn = sqlite3.connect(os.getcwd() + "/addresses.sqlite") + cursor = conn.cursor() + + try: + # Special handling for updating address itself + if "address" in new_data: + new_address = new_data.pop("address") + cursor.execute( + "UPDATE addresses SET address = ? WHERE address = ?", + (new_address, address_id), + ) + + # Update other fields + updates = ", ".join([f"{key} = ?" for key in new_data.keys()]) + parameters = list(new_data.values()) + [ + address_id if "address" not in new_data else new_address + ] + + if updates: + cursor.execute( + f"UPDATE addresses SET {updates} WHERE address = ?", parameters + ) + + conn.commit() + except sqlite3.Error as e: + conn.rollback() + return jsonify({"error": str(e)}), 500 + finally: + conn.close() + + return jsonify({"success": True}), 200 + + +@app.route("/api/v1/resources/addresses/create", methods=["POST"]) +def create_address(): + update_password = os.environ.get("UPDATE_PASSWORD", "default_password") + request_data = request.get_json() + + # Check password + if request_data.get("password") != update_password: + return jsonify({"error": "Unauthorized: Incorrect password"}), 401 + + new_address = request_data.get("address") + alias = request_data.get("alias") + owner = request_data.get("owner") + address_type = request_data.get("type") + + if not all([new_address, alias, owner, address_type]): + return jsonify({"error": "Missing data"}), 400 + + conn = sqlite3.connect(os.getcwd() + "/addresses.sqlite") + cursor = conn.cursor() + + try: + cursor.execute( + "INSERT INTO addresses (address, alias, owner, type) VALUES (?, ?, ?, ?)", + (new_address, alias, owner, address_type), + ) + conn.commit() + except sqlite3.Error as e: + conn.rollback() + return jsonify({"error": str(e)}), 500 + finally: + conn.close() + + return jsonify({"success": True}), 200 + + +@app.route("/api/v1/resources/addresses/delete", methods=["DELETE"]) +def delete_address(): + update_password = os.environ.get("UPDATE_PASSWORD", "default_password") + request_data = request.get_json() + + # Check password + if request_data.get("password") != update_password: + return jsonify({"error": "Unauthorized: Incorrect password"}), 401 + + address_id = request_data.get("id") + + if not address_id: + return jsonify({"error": "Missing data"}), 400 + + conn = sqlite3.connect(os.getcwd() + "/addresses.sqlite") + cursor = conn.cursor() + + try: + cursor.execute("DELETE FROM addresses WHERE address = ?", (address_id,)) + conn.commit() + except sqlite3.Error as e: + conn.rollback() + return jsonify({"error": str(e)}), 500 + finally: + conn.close() + + return jsonify({"success": True}), 200 + +@app.route("/api/v1/resources/addresses", methods=["GET"]) def known_filter(): query_parameters = request.args - addresses = query_parameters.getlist('address') - adtypes = query_parameters.getlist('type') - owners = query_parameters.getlist('owner') - illicit = query_parameters.get('illicit') + addresses = query_parameters.getlist("address") + adtypes = query_parameters.getlist("type") + owners = query_parameters.getlist("owner") + illicit = query_parameters.get("illicit") query = "SELECT * FROM addresses WHERE" to_filter = [] if addresses: - query += ' address IN ({}) AND'.format(', '.join('?' * len(addresses))) + query += " address IN ({}) AND".format(", ".join("?" * len(addresses))) to_filter.extend(addresses) if adtypes: - query += ' type IN ({}) AND'.format(', '.join('?' * len(adtypes))) + query += " type IN ({}) AND".format(", ".join("?" * len(adtypes))) to_filter.extend(adtypes) if owners: - query += ' owner IN ({}) AND'.format(', '.join('?' * len(owners))) + query += " owner IN ({}) AND".format(", ".join("?" * len(owners))) to_filter.extend(owners) if illicit: - query += ' illicit=? AND' + query += " illicit=? AND" to_filter.extend(illicit) if not (addresses or adtypes or owners or illicit): return page_not_found(404) - query = query[:-4] + ';' + query = query[:-4] + ";" conn = sqlite3.connect(os.getcwd() + "/addresses.sqlite") conn.row_factory = dict_factory @@ -106,36 +193,37 @@ def known_filter(): return jsonify(results) -@app.route('/api/v1/resources/intermediaries/all', methods=['GET']) + +@app.route("/api/v1/resources/intermediaries/all", methods=["GET"]) def intermediaries_all(): conn = sqlite3.connect(os.getcwd() + "/addresses.sqlite") conn.row_factory = dict_factory cur = conn.cursor() - all_addresses = cur.execute('SELECT * FROM intermediaries;').fetchall() + all_addresses = cur.execute("SELECT * FROM intermediaries;").fetchall() return jsonify(all_addresses) -@app.route('/api/v1/resources/intermediaries', methods=['GET']) + +@app.route("/api/v1/resources/intermediaries", methods=["GET"]) def intermediaries_filter(): query_parameters = request.args - addresses = query_parameters.getlist('address') - services = query_parameters.getlist('service') + addresses = query_parameters.getlist("address") + services = query_parameters.getlist("service") query = "SELECT * FROM intermediaries WHERE" to_filter = [] if addresses: - query += ' address IN ({}) AND'.format(', '.join('?' * len(addresses))) + query += " address IN ({}) AND".format(", ".join("?" * len(addresses))) to_filter.extend(addresses) if services: - query += ' service IN ({}) AND'.format(', '.join('?' * len(services))) + query += " service IN ({}) AND".format(", ".join("?" * len(services))) to_filter.extend(services) - if not (addresses or services): return page_not_found(404) - query = query[:-4] + ';' + query = query[:-4] + ";" conn = sqlite3.connect(os.getcwd() + "/addresses.sqlite") conn.row_factory = dict_factory @@ -145,15 +233,16 @@ def intermediaries_filter(): return jsonify(results) -@app.route('/api/v1/resources/intermediaries/status', methods=['GET']) + +@app.route("/api/v1/resources/intermediaries/status", methods=["GET"]) def intermediaries_status(): conn = sqlite3.connect(os.getcwd() + "/addresses.sqlite") conn.row_factory = dict_factory cur = conn.cursor() - all_addresses = cur.execute('SELECT * FROM last_run;').fetchall() + all_addresses = cur.execute("SELECT * FROM last_run;").fetchall() return jsonify(all_addresses) -if __name__ == '__main__': - app.run() \ No newline at end of file +if __name__ == "__main__": + app.run() diff --git a/templates/home.html b/templates/home.html new file mode 100644 index 0000000..a27a060 --- /dev/null +++ b/templates/home.html @@ -0,0 +1,57 @@ + + + + + + Banano Address API + + + + + + + + +
+

Welcome to the Banano Address API

+

This API allows you to query Banano address records. Admins can also update and manage records.

+ + + + +

Check out the source code on GitHub.

+
+ + + + + \ No newline at end of file diff --git a/templates/manage_records.html b/templates/manage_records.html new file mode 100644 index 0000000..16821ca --- /dev/null +++ b/templates/manage_records.html @@ -0,0 +1,117 @@ + + + + + + Manage Address Records + + + + + + + +
+

Create Address Record

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +

Delete Address Record

+
+
+ + +
+
+ + +
+ +
+ +
+
+ + + + + + \ No newline at end of file diff --git a/templates/update_form.html b/templates/update_form.html new file mode 100644 index 0000000..70c38e9 --- /dev/null +++ b/templates/update_form.html @@ -0,0 +1,154 @@ + + + + + + Update and Query Records + + + + + + + +
+

Query Address Records

+
+
+ + +
+ +
+ +
+ +

Update Record

+ + +
+
+ + + + + + \ No newline at end of file