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/FireWalletBrowser.bsdesign b/FireWalletBrowser.bsdesign new file mode 100644 index 0000000..fca0fa5 Binary files /dev/null and b/FireWalletBrowser.bsdesign differ diff --git a/README.md b/README.md index 2d2399f..35db089 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,32 @@ If you have HSD running on a different IP/container sudo docker run -p 5000:5000 -e hsd_api=yourapikeyhere -e hsd_ip=hsdcontainer git.woodburn.au/nathanwoodburn/firewallet:latest ``` +## Features +- Basic wallet functionality + - Create new wallet + - Import wallet from seed + - Send HNS + - Receive HNS + - Have multiple wallets + - View transactions + - View balance + - View wallet domains +- Domain management + - Transfer domains + - DNS Editor + - Renew domains +- Auctions + - Send open + - Send bid + - Send reveal + - Send redeem +- Download a list of all domains +- Resend all pending transactions +- Rescan +- Zap pending transactions +- View xPub +- Custom plugin support + ## Themes Set a theme in the .env file **Available themes** diff --git a/account.py b/account.py index 0b14571..d8e36b1 100644 --- a/account.py +++ b/account.py @@ -84,6 +84,31 @@ def createWallet(account: str, password: str): "password": password } +def importWallet(account: str, password: str,seed: str): + # Import the wallet + data = { + "passphrase": password, + "mnemonic": seed, + } + + response = requests.put(f"http://x:{APIKEY}@{ip}:12039/wallet/{account}",json=data) + print(response) + print(response.json()) + + if response.status_code != 200: + return { + "error": { + "message": "Error creating account" + } + } + + return { + "seed": seed, + "account": account, + "password": password + } + + def listWallets(): # List the wallets response = hsw.listWallets() @@ -461,7 +486,21 @@ def finalize(account,domain): } try: - response = hsw.sendFINALIZE(account_name,password,domain) + response = hsw.rpc_selectWallet(account_name) + if response['error'] is not None: + return { + "error": { + "message": response['error']['message'] + } + } + response = hsw.rpc_walletPassphrase(password,10) + if response['error'] is not None: + return { + "error": { + "message": response['error']['message'] + } + } + response = hsw.rpc_sendFINALIZE(domain) return response except Exception as e: return { @@ -626,9 +665,8 @@ def getxPub(account): #endregion -def generateReport(account): +def generateReport(account,format="{name},{expiry},{value},{maxBid}"): domains = getDomains(account) - format = str('{name},{expiry},{value},{maxBid}') lines = [format.replace("{","").replace("}","")] for domain in domains: @@ -650,4 +688,7 @@ def generateReport(account): line = line.replace("{openHeight}",str(domain['height'])) lines.append(line) - return lines \ No newline at end of file + return lines + +def convertHNS(value: int): + return value/1000000 \ No newline at end of file diff --git a/main.py b/main.py index 46d0f03..4d6c8f0 100644 --- a/main.py +++ b/main.py @@ -10,6 +10,8 @@ import re from flask_qrcode import QRcode import domainLookup import urllib.parse +import importlib +import plugin as plugins_module dotenv.load_dotenv() @@ -90,10 +92,17 @@ def index(): domain_count = len(domains) domains = render.domains(domains) + plugins = "" + dashFunctions = plugins_module.getDashboardFunctions() + for function in dashFunctions: + functionOutput = plugins_module.runPluginFunction(function["plugin"],function["function"],{},account_module.check_account(request.cookies.get("account"))) + plugins += render.plugin_output_dash(functionOutput,plugins_module.getPluginFunctionReturns(function["plugin"],function["function"])) + + return render_template("index.html", account=account, available=available, - total=total, pending=pending, domains=domains, + total=total, pending=pending, domains=domains, plugins=plugins, domain_count=domain_count, sync=account_module.getNodeSync(), sort_price=sort_price,sort_expiry=sort_expiry, sort_domain=sort_domain,sort_price_next=sort_price_next, @@ -319,10 +328,20 @@ def search(): dns = render.dns(dns) txs = render.txs(txs) + + plugins = "
" + # Execute domain plugins + searchFunctions = plugins_module.getSearchFunctions() + for function in searchFunctions: + functionOutput = plugins_module.runPluginFunction(function["plugin"],function["function"],{"domain":search_term},account_module.check_account(request.cookies.get("account"))) + plugins += render.plugin_output(functionOutput,plugins_module.getPluginFunctionReturns(function["plugin"],function["function"])) + + plugins += "
" + return render_template("search.html", account=account, sync=account_module.getNodeSync(), search_term=search_term,domain=domain['info']['name'], raw=domain,state=state, next=next, owner=owner, - dns=dns, txs=txs) + dns=dns, txs=txs,plugins=plugins) @app.route('/manage/') def manage(domain: str): @@ -370,11 +389,21 @@ def manage(domain: str): else: finalize_time = "now" + plugins = "
" + # Execute domain plugins + domainFunctions = plugins_module.getDomainFunctions() + for function in domainFunctions: + functionOutput = plugins_module.runPluginFunction(function["plugin"],function["function"],{"domain":domain},account_module.check_account(request.cookies.get("account"))) + plugins += render.plugin_output(functionOutput,plugins_module.getPluginFunctionReturns(function["plugin"],function["function"])) + + plugins += "
" + + return render_template("manage.html", account=account, sync=account_module.getNodeSync(), error=errorMessage, address=address, domain=domain,expiry=expiry, dns=dns, raw_dns=urllib.parse.quote(raw_dns), - finalize_time=finalize_time) + finalize_time=finalize_time,plugins=plugins) @app.route('/manage//finalize') @@ -390,11 +419,11 @@ def finalize(domain: str): domain = domain.lower() print(domain) response = account_module.finalize(request.cookies.get("account"),domain) - if 'error' in response: + if response['error'] != None: print(response) return redirect("/manage/" + domain + "?error=" + response['error']['message']) - return redirect("/success?tx=" + response['hash']) + return redirect("/success?tx=" + response['result']['hash']) @app.route('/manage//cancel') def cancelTransfer(domain: str): @@ -820,8 +849,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 +906,6 @@ def settings_action(action): #endregion -#endregion #region Account @@ -966,6 +994,50 @@ def register(): response.set_cookie("account", account+":"+password) return response +@app.route('/import-wallet', methods=["POST"]) +def import_wallet(): + # Get the account and password + account = request.form.get("name") + password = request.form.get("password") + repeatPassword = request.form.get("password_repeat") + seed = request.form.get("seed") + + # Check if the passwords match + if password != repeatPassword: + return render_template("import-wallet.html", + error="Passwords do not match", + name=account,password=password,password_repeat=repeatPassword, + seed=seed) + + # Check if the account is valid + if account.count(":") > 0: + return render_template("import-wallet.html", + error="Invalid account", + name=account,password=password,password_repeat=repeatPassword, + seed=seed) + + # List wallets + wallets = account_module.listWallets() + if account in wallets: + return render_template("import-wallet.html", + error="Account already exists", + name=account,password=password,password_repeat=repeatPassword, + seed=seed) + + # Create the account + response = account_module.importWallet(account,password,seed) + + if 'error' in response: + return render_template("import-wallet.html", + error=response['error'], + name=account,password=password,password_repeat=repeatPassword, + seed=seed) + + + # Set the cookie + response = make_response(redirect("/")) + response.set_cookie("account", account+":"+password) + return response @app.route('/report') def report(): @@ -979,6 +1051,102 @@ 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") + + plugins = render.plugins(plugins_module.listPlugins()) + + 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 plugins_module.pluginExists(plugin): + return redirect("/plugins") + + data = plugins_module.getPluginData(plugin) + + functions = plugins_module.getPluginFunctions(plugin) + 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'], + author=data['author'],version=data['version'], + 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 plugins_module.pluginExists(plugin): + return redirect("/plugins") + + data = plugins_module.getPluginData(plugin) + + # Get plugin/main.py listfunctions() + if function in plugins_module.getPluginFunctions(plugin): + inputs = plugins_module.getPluginFunctionInputs(plugin,function) + request_data = {} + for input in inputs: + request_data[input] = request.form.get(input) + + if inputs[input]['type'] == "address": + # Handle hip2 + address_check = account_module.check_address(request_data[input],True,True) + if not address_check: + return redirect("/plugin/" + plugin + "?error=Invalid address") + request_data[input] = address_check + elif inputs[input]['type'] == "dns": + # Handle URL encoding of DNS + request_data[input] = urllib.parse.unquote(request_data[input]) + + + + + response = plugins_module.runPluginFunction(plugin,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,plugins_module.getPluginFunctionReturns(plugin,function)) + + 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"}) + +#endregion + + #region Assets and default pages @app.route('/qr/') def qr(data): diff --git a/plugin.py b/plugin.py new file mode 100644 index 0000000..10d784d --- /dev/null +++ b/plugin.py @@ -0,0 +1,101 @@ +import os +import json +import importlib + + + +def listPlugins(): + plugins = [] + for file in os.listdir("plugins"): + if file.endswith(".py"): + if file != "main.py": + plugin = importlib.import_module("plugins."+file[:-3]) + details = plugin.info + details["link"] = file[:-3] + plugins.append(details) + return plugins + + +def pluginExists(plugin: str): + for file in os.listdir("plugins"): + if file == plugin+".py": + return True + return False + +def getPluginData(plugin: str): + plugin = importlib.import_module("plugins."+plugin) + return plugin.info + +def getPluginFunctions(plugin: str): + plugin = importlib.import_module("plugins."+plugin) + return plugin.functions + +def runPluginFunction(plugin: str, function: str, params: dict, authentication: str): + plugin_module = importlib.import_module("plugins."+plugin) + if function not in plugin_module.functions: + return {"error": "Function not found"} + + if not hasattr(plugin_module, function): + return {"error": "Function not found"} + + # Get the function object from the plugin module + plugin_function = getattr(plugin_module, function) + + # Call the function with provided parameters + try: + result = plugin_function(params, authentication) + return result + except Exception as e: + print(f"Error running plugin: {e}") + return {"error": str(e)} + # return plugin.runFunction(function, params, authentication) + +def getPluginFunctionInputs(plugin: str, function: str): + plugin = importlib.import_module("plugins."+plugin) + return plugin.functions[function]["params"] + +def getPluginFunctionReturns(plugin: str, function: str): + plugin = importlib.import_module("plugins."+plugin) + return plugin.functions[function]["returns"] + +def getDomainFunctions(): + plugins = listPlugins() + domainFunctions = [] + for plugin in plugins: + functions = getPluginFunctions(plugin["link"]) + for function in functions: + if functions[function]["type"] == "domain": + domainFunctions.append({ + "plugin": plugin["link"], + "function": function, + "description": functions[function]["description"] + }) + return domainFunctions + +def getSearchFunctions(): + plugins = listPlugins() + searchFunctions = [] + for plugin in plugins: + functions = getPluginFunctions(plugin["link"]) + for function in functions: + if functions[function]["type"] == "search": + searchFunctions.append({ + "plugin": plugin["link"], + "function": function, + "description": functions[function]["description"] + }) + return searchFunctions + +def getDashboardFunctions(): + plugins = listPlugins() + dashboardFunctions = [] + for plugin in plugins: + functions = getPluginFunctions(plugin["link"]) + for function in functions: + if functions[function]["type"] == "dashboard": + dashboardFunctions.append({ + "plugin": plugin["link"], + "function": function, + "description": functions[function]["description"] + }) + return dashboardFunctions \ No newline at end of file diff --git a/plugins.md b/plugins.md new file mode 100644 index 0000000..2142ee4 --- /dev/null +++ b/plugins.md @@ -0,0 +1,62 @@ +# Plugins + +## Types +### Default +Type: `default` +This is the default type and is used when no type is specified. +This type is displayed in the plugin page only. +This is the onlu type of plugin that takes user input + +### Manage & Search +For manage page use type: `domain` +For search page use type: `search` + +This type is used for domain plugins. It shows in the manage domain page or the search page. +It gets the `domain` paramater as the only input (in addition to authentication) + +### Dashboard +This type is used for dashboard plugins. +It shows in the dashboard page. It doesn't get any inputs other than the authentication + + +## Inputs + +### Plain Text +Type: `text` + +### Long Text +Type: `longText` + +### Number +Type: `number` + + +### Checkbox +Type: `checkbox` + +### Address +Type: `address` +This will handle hip2 resolution for you so the function will always receive a valid address + +### DNS +Type: `dns` +This isn't done yet but use it over text as it includes parsing + + + +## Outputs +### Plain Text +Type: `text` + + +### List +Type: `list` +This is a list if text items (or HTML items) + +### Transaction hash +Type: `tx` +This will display the hash and links to explorers + +### DNS records +Type: `dns` +This will display DNS in a table format diff --git a/plugins/example.py b/plugins/example.py new file mode 100644 index 0000000..ccfdc1b --- /dev/null +++ b/plugins/example.py @@ -0,0 +1,175 @@ +import json +import account +import requests + + +# Plugin Data +info = { + "name": "Example Plugin", + "description": "This is a plugin to be used as an example", + "version": "1.0", + "author": "Nathan.Woodburn/" +} + + +# Functions +functions = { + "search":{ + "name": "Search Owned", + "type": "default", + "description": "Search for owned domains containing a string", + "params": { + "search": { + "name":"Search string", + "type":"text" + } + }, + "returns": { + "domains": + { + "name": "List of owned domains", + "type": "list" + } + } + }, + "transfer":{ + "name": "Bulk Transfer Domains", + "type": "default", + "description": "Transfer domains to another wallet", + "params": { + "address": { + "name":"Address to transfer to", + "type":"address" + }, + "domains": { + "name":"List of domains to transfer", + "type":"longText" + } + }, + "returns": { + "hash": { + "name": "Hash of the transaction", + "type": "tx" + }, + "address":{ + "name": "Address of the new owner", + "type": "text" + } + } + }, + "dns":{ + "name": "Set DNS for Domains", + "type": "default", + "description": "Set DNS for domains", + "params": { + "domains": { + "name":"List of domains to set DNS for", + "type":"longText" + }, + "dns": { + "name":"DNS", + "type":"dns" + } + }, + "returns": { + "hash": { + "name": "Hash of the transaction", + "type": "tx" + }, + "dns":{ + "name": "DNS", + "type": "dns" + } + } + }, + "niami": { + "name": "Niami info", + "type": "domain", + "description": "Check the domains niami rating", + "params": {}, + "returns": { + "rating": + { + "name": "Niami Rating", + "type": "text" + } + } + }, + "niamiSearch": { + "name": "Niami info", + "type": "search", + "description": "Check the domains niami rating", + "params": {}, + "returns": { + "rating": + { + "name": "Niami Rating", + "type": "text" + } + } + }, + "connections":{ + "name": "HSD Connections", + "type": "dashboard", + "description": "Show the number of connections the HSD node is connected to", + "params": {}, + "returns": { + "connections": + { + "name": "HSD Connections", + "type": "text" + } + } + } +} + +def check(params, authentication): + domains = params["domains"] + 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(params, authentication): + search = params["search"] + 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} + + +def transfer(params, authentication): + address = params["address"] + return {"hash":"f921ffe1bb01884bf515a8079073ee9381cb93a56b486694eda2cce0719f27c0","address":address} + +def dns(params,authentication): + dns = params["dns"] + return {"hash":"f921ffe1bb01884bf515a8079073ee9381cb93a56b486694eda2cce0719f27c0","dns":dns} + +def niami(params, authentication): + domain = params["domain"] + print(domain) + response = requests.get(f"https://api.handshake.niami.io/domain/{domain}") + print(response.text) + data = response.json()["data"] + rating = str(data["rating"]["score"]) + " (" + data["rating"]["rarity"] + ")" + return {"rating":rating} + +def niamiSearch(params, authentication): + return niami(params, authentication) + + +def connections(params,authentication): + outbound = account.hsd.getInfo()['pool']['outbound'] + return {"connections": outbound} \ No newline at end of file diff --git a/render.py b/render.py index 49e9a0e..d430c76 100644 --- a/render.py +++ b/render.py @@ -1,6 +1,7 @@ import datetime import json import urllib.parse +from flask import render_template def domains(domains): html = '' @@ -178,4 +179,124 @@ def wallets(wallets): html = '' for wallet in wallets: html += f'' + 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'] + returnsRaw = functions[function]['returns'] + + returns = "" + for output in returnsRaw: + returns += f"{returnsRaw[output]['name']}, " + + returns = returns.removesuffix(', ') + + functionType = "default" + if "type" in functions[function]: + functionType = functions[function]["type"] + + + html += f'
    ' + html += f'
    ' + html += f'

    {name}

    ' + html += f'
    {description}
    ' + html += f'
    Function type: {functionType.capitalize()}
    ' + + if functionType != "default": + html += f'

    Returns: {returns}

    ' + html += f'
    ' + html += f'
    ' + continue + + # 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'
    ' + elif paramType == "address": + # render components/address.html + address = render_template('components/address.html', paramName=paramName, param=param) + html += address + elif paramType == "dns": + html += render_template('components/dns-input.html', paramName=paramName, param=param) + + + + + 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: + 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}

    ' + elif returns[returnOutput]["type"] == "tx": + html += render_template('components/tx.html', tx=output) + elif returns[returnOutput]["type"] == "dns": + output = json.loads(output) + html += render_template('components/dns-output.html', dns=dns(output)) + + + html += f'
    ' + html += f'
    ' + return html + +def plugin_output_dash(outputs, returns): + + html = '' + + for returnOutput in returns: + html += render_template('components/dashboard-plugin.html', name=returns[returnOutput]["name"], output=outputs[returnOutput]) + + + 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/components/address.html b/templates/components/address.html new file mode 100644 index 0000000..598e363 --- /dev/null +++ b/templates/components/address.html @@ -0,0 +1,40 @@ +
    + + + + +
    \ No newline at end of file diff --git a/templates/components/dashboard-plugin.html b/templates/components/dashboard-plugin.html new file mode 100644 index 0000000..c6fdb75 --- /dev/null +++ b/templates/components/dashboard-plugin.html @@ -0,0 +1,8 @@ +
    +
    +
    +
    {{name}}
    +
    {{output}}
    +
    +
    +
    \ No newline at end of file diff --git a/templates/components/dns-input.html b/templates/components/dns-input.html new file mode 100644 index 0000000..690ea4e --- /dev/null +++ b/templates/components/dns-input.html @@ -0,0 +1,5 @@ +

    TODO DNS LATER

    +

    For a temporary work around edit any domains DNS and before pressing save

    +

    Copy the text in the url after ?dns=

    + + \ No newline at end of file diff --git a/templates/components/dns-output.html b/templates/components/dns-output.html new file mode 100644 index 0000000..3c21c02 --- /dev/null +++ b/templates/components/dns-output.html @@ -0,0 +1,13 @@ +
    + + + + + + + + + {{dns | safe}} + +
    +
    \ No newline at end of file diff --git a/templates/components/tx.html b/templates/components/tx.html new file mode 100644 index 0000000..c1e0c1e --- /dev/null +++ b/templates/components/tx.html @@ -0,0 +1,4 @@ +TX: {{tx}} +Check your transaction on a block explorer +Niami +3xpl \ No newline at end of file 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/import-wallet.html b/templates/import-wallet.html new file mode 100644 index 0000000..a103c43 --- /dev/null +++ b/templates/import-wallet.html @@ -0,0 +1,52 @@ + + + + + + + Import Wallet - FireWallet + + + + + + + + + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    {{error}}

    +
    +

    Import a wallet!

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index c4a3fba..1f79865 100644 --- a/templates/index.html +++ b/templates/index.html @@ -30,7 +30,7 @@ - +
    @@ -126,7 +126,7 @@ - + {{plugins|safe}}
    diff --git a/templates/login.html b/templates/login.html index 1ea980d..5c53471 100644 --- a/templates/login.html +++ b/templates/login.html @@ -37,7 +37,8 @@

    - + +
    diff --git a/templates/manage.html b/templates/manage.html index cde165f..b83db1b 100644 --- a/templates/manage.html +++ b/templates/manage.html @@ -30,7 +30,7 @@ - +
    @@ -68,7 +68,7 @@
    Expires in {{expiry}} days
    - + {{plugins|safe}}
    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..38db15c --- /dev/null +++ b/templates/plugin.html @@ -0,0 +1,81 @@ + + + + + + + {{name}} - FireWallet + + + + + + + + + + + + + +
    + +
    +
    + +

    {{error}}

    +
    +

    {{name}}

    +

    {{description}}

    +
    Author: {{author}}
    Version: {{version}}
    {{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/register.html b/templates/register.html index 655396b..ffec1a3 100644 --- a/templates/register.html +++ b/templates/register.html @@ -27,14 +27,14 @@

    {{error}}

    -

    Create an Account!

    +

    Create a new wallet!

    -
    +

    diff --git a/templates/search.html b/templates/search.html index 3a5c9ab..373715d 100644 --- a/templates/search.html +++ b/templates/search.html @@ -30,7 +30,7 @@ - +
    @@ -68,7 +68,7 @@
    Owner: {{owner}}
    ManageAuction - + {{plugins|safe}}
    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 @@ - +