From 5bb4e1bc4c4d11207a9b67369943fc5b8c544a13 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Fri, 2 Feb 2024 21:40:02 +1100 Subject: [PATCH] feat: Initial custom plugins --- .gitignore | 2 + main.py | 132 +++++++++++++++++++++++++++++++- plugins/example/example.json | 4 + plugins/example/main.py | 76 ++++++++++++++++++ render.py | 84 +++++++++++++++++++- templates/404.html | 2 +- templates/auction.html | 2 +- templates/confirm-password.html | 2 +- templates/confirm.html | 2 +- templates/index.html | 2 +- templates/manage.html | 2 +- templates/message.html | 2 +- templates/plugin-output.html | 79 +++++++++++++++++++ templates/plugin.html | 80 +++++++++++++++++++ templates/plugins.html | 80 +++++++++++++++++++ templates/receive.html | 2 +- templates/search.html | 2 +- templates/send.html | 2 +- templates/settings.html | 2 +- templates/success.html | 2 +- templates/tx.html | 2 +- 21 files changed, 546 insertions(+), 17 deletions(-) create mode 100644 plugins/example/example.json create mode 100644 plugins/example/main.py create mode 100644 templates/plugin-output.html create mode 100644 templates/plugin.html create mode 100644 templates/plugins.html diff --git a/.gitignore b/.gitignore index 9a6d587..c841779 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .env __pycache__/ + +templates/assets/css/styles.min.css diff --git a/main.py b/main.py index 46d0f03..84b6fc3 100644 --- a/main.py +++ b/main.py @@ -10,6 +10,7 @@ import re from flask_qrcode import QRcode import domainLookup import urllib.parse +import importlib dotenv.load_dotenv() @@ -820,8 +821,8 @@ def reveal_auction(domain): return redirect("/auction/" + domain + "?message=" + response['error']['message']) return redirect("/success?tx=" + response['hash']) - -#region settings +#endregion +#region Settings @app.route('/settings') def settings(): # Check if the user is logged in @@ -877,7 +878,6 @@ def settings_action(action): #endregion -#endregion #region Account @@ -979,6 +979,132 @@ def report(): #endregion +#region Plugins +@app.route('/plugins') +def plugins_index(): + # Check if the user is logged in + if request.cookies.get("account") is None: + return redirect("/login") + + account = account_module.check_account(request.cookies.get("account")) + if not account: + return redirect("/logout") + + plugin_links = os.listdir("plugins") + plugins = [] + for plugin in plugin_links: + if os.path.isdir("plugins/" + plugin): + if os.path.isfile("plugins/" + plugin + "/"+plugin+".json"): + with open("plugins/" + plugin + "/"+plugin+".json") as f: + data = json.load(f) + data['link'] = plugin + if 'name' not in data: + data['name'] = plugin + if 'description' not in data: + data['description'] = "No description provided" + plugins.append(data) + + plugins = render.plugins(plugins) + + return render_template("plugins.html", account=account, sync=account_module.getNodeSync(), + plugins=plugins) + +@app.route('/plugin/') +def plugin(plugin): + # Check if the user is logged in + if request.cookies.get("account") is None: + return redirect("/login") + + account = account_module.check_account(request.cookies.get("account")) + if not account: + return redirect("/logout") + + if not os.path.isdir("plugins/" + plugin): + return redirect("/plugins") + + if not os.path.isfile("plugins/" + plugin + "/"+plugin+".json"): + return redirect("/plugins") + + with open("plugins/" + plugin + "/"+plugin+".json") as f: + data = json.load(f) + data['link'] = plugin + if 'name' not in data: + data['name'] = plugin + if 'description' not in data: + data['description'] = "No description provided" + + + functions = [] + + if os.path.isfile("plugins/" + plugin + "/main.py"): + # Get plugin/main.py listfunctions() + print("Loading plugin: " + plugin) + module = importlib.import_module("plugins." + plugin + ".main") + functions = module.listFunctions() + functions = render.plugin_functions(functions,plugin) + + error = request.args.get("error") + if error == None: + error = "" + + return render_template("plugin.html", account=account, sync=account_module.getNodeSync(), + name=data['name'],description=data['description'],functions=functions, + error=error) + +@app.route('/plugin//', methods=["POST"]) +def plugin_function(plugin,function): + # Check if the user is logged in + if request.cookies.get("account") is None: + return redirect("/login") + + account = account_module.check_account(request.cookies.get("account")) + if not account: + return redirect("/logout") + + if not os.path.isdir("plugins/" + plugin): + return redirect("/plugins") + + if not os.path.isfile("plugins/" + plugin + "/"+plugin+".json"): + return redirect("/plugins") + + with open("plugins/" + plugin + "/"+plugin+".json") as f: + data = json.load(f) + data['link'] = plugin + if 'name' not in data: + data['name'] = plugin + if 'description' not in data: + data['description'] = "No description provided" + + if os.path.isfile("plugins/" + plugin + "/main.py"): + # Get plugin/main.py listfunctions() + print("Loading plugin: " + plugin) + module = importlib.import_module("plugins." + plugin + ".main") + if function in module.listFunctions(): + inputs = module.listFunctions()[function]["params"] + request_data = {} + for input in inputs: + request_data[input] = request.form.get(input) + + response = module.runFunction(function,request_data,request.cookies.get("account")) + if not response: + return redirect("/plugin/" + plugin + "?error=An error occurred") + if 'error' in response: + return redirect("/plugin/" + plugin + "?error=" + response['error']) + + response = render.plugin_output(response,module.listFunctions()[function]["returns"]) + + return render_template("plugin-output.html", account=account, sync=account_module.getNodeSync(), + name=data['name'],description=data['description'],output=response) + + + else: + return jsonify({"error": "Function not found"}) + + return jsonify({"error": "Plugin not found"}) + +#endregion + + #region Assets and default pages @app.route('/qr/') def qr(data): diff --git a/plugins/example/example.json b/plugins/example/example.json new file mode 100644 index 0000000..6773397 --- /dev/null +++ b/plugins/example/example.json @@ -0,0 +1,4 @@ +{ + "name":"Example Plugin", + "description":"This is an example plugin" +} \ No newline at end of file diff --git a/plugins/example/main.py b/plugins/example/main.py new file mode 100644 index 0000000..0d22500 --- /dev/null +++ b/plugins/example/main.py @@ -0,0 +1,76 @@ +import json +import account + + +functions = { + "check": { + "name": "Domain Check", + "description": "Check if domains in file are owned by the wallet", + "params": { + "domains": { + "name":"File of domains to check", + "type":"longText" + } + }, + "returns": { + "domains": + { + "name": "List of owned domains", + "type": "list" + } + } + }, + "search":{ + "name": "Search Owned", + "description": "Search for owned domains containing a string", + "params": { + "search": { + "name":"Search string", + "type":"text" + } + }, + "returns": { + "domains": + { + "name": "List of owned domains", + "type": "list" + } + } + } +} + +def listFunctions(): + return functions + +def runFunction(function, params, authentication): + if function == "check": + return check(params['domains'], authentication) + elif function == "search": + return search(params['search'], authentication) + else: + return "Function not found" + + +def check(domains, authentication): + domains = domains.splitlines() + + wallet = authentication.split(":")[0] + owned = account.getDomains(wallet) + # Only keep owned domains ["name"] + ownedNames = [domain["name"] for domain in owned] + + domains = [domain for domain in domains if domain in ownedNames] + + + return {"domains": domains} + +def search(search, authentication): + wallet = authentication.split(":")[0] + owned = account.getDomains(wallet) + # Only keep owned domains ["name"] + ownedNames = [domain["name"] for domain in owned] + + domains = [domain for domain in ownedNames if search in domain] + + return {"domains": domains} + diff --git a/render.py b/render.py index 49e9a0e..b667632 100644 --- a/render.py +++ b/render.py @@ -178,4 +178,86 @@ def wallets(wallets): html = '' for wallet in wallets: html += f'' - return html \ No newline at end of file + return html + +def plugins(plugins): + html = '' + for plugin in plugins: + name = plugin['name'] + link = plugin['link'] + + html += f'
  • {name}
  • ' + return html + +def plugin_functions(functions, pluginName): + html = '' + for function in functions: + name = functions[function]['name'] + description = functions[function]['description'] + params = functions[function]['params'] + returns = functions[function]['returns'] + html += f'
    ' + html += f'
    ' + html += f'

    {name}

    ' + html += f'
    {description}
    ' + + # Form + html += f'
    ' + for param in params: + html += f'
    ' + paramName = params[param]["name"] + paramType = params[param]["type"] + if paramType == "text": + html += f'' + html += f'' + elif paramType == "longText": + html += f'' + html += f'' + elif paramType == "number": + html += f'' + html += f'' + elif paramType == "checkbox": + html += f'
    ' + + html += f'
    ' + + html += f'' + html += f'
    ' + # For debugging + html += f'

    Returns: {returns}

    ' + html += f'
    ' + html += f'
    ' + + + return html + +def plugin_output(outputs, returns): + + html = '' + + for returnOutput in returns: + print(returns) + + html += f'
    ' + html += f'
    ' + html += f'

    {returns[returnOutput]["name"]}

    ' + + # Get the output + output = outputs[returnOutput] + if returns[returnOutput]["type"] == "list": + html += f'
      ' + for item in output: + html += f'
    • {item}
    • ' + html += f'
    ' + elif returns[returnOutput]["type"] == "text": + html += f'

    {output}

    ' + + + html += f'
    ' + html += f'
    ' + + + + return html + + \ No newline at end of file diff --git a/templates/404.html b/templates/404.html index 59334f2..e3729a8 100644 --- a/templates/404.html +++ b/templates/404.html @@ -30,7 +30,7 @@ - +
    diff --git a/templates/auction.html b/templates/auction.html index efcea85..e148f5b 100644 --- a/templates/auction.html +++ b/templates/auction.html @@ -30,7 +30,7 @@ - +
    diff --git a/templates/confirm-password.html b/templates/confirm-password.html index 2939e6d..9e6a515 100644 --- a/templates/confirm-password.html +++ b/templates/confirm-password.html @@ -30,7 +30,7 @@ - +
    diff --git a/templates/confirm.html b/templates/confirm.html index ecd63f7..e0c4161 100644 --- a/templates/confirm.html +++ b/templates/confirm.html @@ -30,7 +30,7 @@ - +
    diff --git a/templates/index.html b/templates/index.html index c4a3fba..eb396d8 100644 --- a/templates/index.html +++ b/templates/index.html @@ -30,7 +30,7 @@ - +
    diff --git a/templates/manage.html b/templates/manage.html index cde165f..42300d0 100644 --- a/templates/manage.html +++ b/templates/manage.html @@ -30,7 +30,7 @@ - +
    diff --git a/templates/message.html b/templates/message.html index bc18201..020cedd 100644 --- a/templates/message.html +++ b/templates/message.html @@ -30,7 +30,7 @@ - +
    diff --git a/templates/plugin-output.html b/templates/plugin-output.html new file mode 100644 index 0000000..250a1be --- /dev/null +++ b/templates/plugin-output.html @@ -0,0 +1,79 @@ + + + + + + + {{name}} - FireWallet + + + + + + + + + + + + + +
    + +
    +
    + +
    +

    {{name}}

    +

    {{description}}

    {{output|safe}} +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/templates/plugin.html b/templates/plugin.html new file mode 100644 index 0000000..af6d813 --- /dev/null +++ b/templates/plugin.html @@ -0,0 +1,80 @@ + + + + + + + {{name}} - FireWallet + + + + + + + + + + + + + +
    + +
    +
    + +

    {{error}}

    +
    +

    {{name}}

    +

    {{description}}

    {{functions|safe}} +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/templates/plugins.html b/templates/plugins.html new file mode 100644 index 0000000..bc90a8e --- /dev/null +++ b/templates/plugins.html @@ -0,0 +1,80 @@ + + + + + + + Plugins - FireWallet + + + + + + + + + + + + + +
    + +
    +
    + +
    +

    Plugins

      + {{plugins | safe}} +
    +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/templates/receive.html b/templates/receive.html index 04abd98..05166ad 100644 --- a/templates/receive.html +++ b/templates/receive.html @@ -30,7 +30,7 @@ - +
    diff --git a/templates/search.html b/templates/search.html index 3a5c9ab..f494772 100644 --- a/templates/search.html +++ b/templates/search.html @@ -30,7 +30,7 @@ - +
    diff --git a/templates/send.html b/templates/send.html index bc39855..105420c 100644 --- a/templates/send.html +++ b/templates/send.html @@ -30,7 +30,7 @@ - +
    diff --git a/templates/settings.html b/templates/settings.html index 401a9d8..e7b9efa 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -30,7 +30,7 @@ - +
    diff --git a/templates/success.html b/templates/success.html index df64a0c..dc459a8 100644 --- a/templates/success.html +++ b/templates/success.html @@ -30,7 +30,7 @@ - +
    diff --git a/templates/tx.html b/templates/tx.html index 03d463d..3325ef8 100644 --- a/templates/tx.html +++ b/templates/tx.html @@ -30,7 +30,7 @@ - +