diff --git a/request-invoice/README.md b/request-invoice/README.md index 0b69474..3b4490e 100644 --- a/request-invoice/README.md +++ b/request-invoice/README.md @@ -3,32 +3,25 @@ This plugin starts a minimal, rate limited HTTP Server and returns a invoice on the following GET request: +``/invoice//`` + ``` -/invoice// - -localhost:8809/invoice/100000/some-text-here - +$ lightning-cli invoiceserver +started server successfully on port 8089 +$ curl http://localhost:8809/invoice/100000/some-text-here {"bolt11":"lnbc100000n1p0yr9sxpp50t9rnvw5slcjn6m97fmujpp9xd6w0h26dpvajcv8xwhu42xk5vlqdq8vej8xcgxqyj...","expires_at":1581961350,"payment_hash":"7aca39b1d487f129eb65f277c904253374e7dd5a6859d9618733afcaa8d6a33e", ...} ``` The webserver is rate-limited, meaning that only a certain amount of request per minute is allowed (to prevent DoS attacks). -The json invoice can be used by other services, for example a service for tipping, donations, ... - - -Command line options are registered by the plugin and can be used to customize its behavior: - -| Command line option | Description | -|------------------------|---------------------------------------------------------------------| -| `--donation-autostart` | Should the donation server start automatically? (default: `true`) | -| `--donation-web-port` | Which port should the donation server listen to? (default: `8809`) | +The json invoice can be used by other services, for example a service for tipping, donations, PoS devices, ... Once the plugin is active you can run `lightning-cli help invoiceserver` to learn about the command line API: -Controls a invoice server with `start`/`stop`/`restart`/`list` on `port`. - - +Commands: `start`/`stop`/`restart`/`status`. +In the file `.lightning/bitcoin/.env` the `port` can be set (default 8089). +A (reverse)proxy or tunnel should be used in production. diff --git a/request-invoice/requestinvoice.py b/request-invoice/requestinvoice.py index 0957683..f804a30 100755 --- a/request-invoice/requestinvoice.py +++ b/request-invoice/requestinvoice.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import multiprocessing - from flask import Flask from flask import jsonify from flask_limiter import Limiter @@ -12,42 +11,44 @@ from tornado.ioloop import IOLoop from pyln.client import LightningRpc, Plugin from time import time -from random import random +import os, uuid +from dotenv import load_dotenv, find_dotenv +from pathlib import Path -# change this -secret= 'caba27ba-45c7-4495-aa53-fd6a5866fbd8' - +# read or create file .lightning/bitcoin/.env +env_path = Path('.') / '.env' +if not env_path.exists(): + env_path.open('a') + env_path.write_text("FLASKSECRET="+uuid.uuid4().hex +"\nFLASKPORT=8809"+"\nAUTOSTART=0") +load_dotenv(str(env_path)) plugin = Plugin() app = Flask(__name__) limiter = Limiter( app, key_func=get_remote_address, - default_limits=["200 per day", "50 per hour"] + default_limits=["2000 per day", "20 per minute"] ) - @limiter.limit("20 per minute") +@app.route('/invoice//') def getinvoice(amount, description): - global plugin - label = "ln-getinvoice-{}".format(random()) - invoice = plugin.rpc.invoice(int(amount)*1000, label, description) - return invoice - + global plugin + label = "ln-getinvoice-{}".format(uuid.uuid4()) + invoice = plugin.rpc.invoice(int(amount)*1000, label, description) + return invoice def worker(port): - app.config['SECRET_KEY'] = secret - app.add_url_rule('/invoice//', 'getinvoice', getinvoice) + print('starting server') + app.config['SECRET_KEY'] = os.getenv("FLASKSECRET", default = uuid.uuid4()) http_server = HTTPServer(WSGIContainer(app)) http_server.listen(port) IOLoop.instance().start() return - jobs = {} - def start_server(port): if port in jobs: return False, "server already running" @@ -58,7 +59,6 @@ def start_server(port): jobs[port] = p p.start() - return True def stop_server(port): @@ -69,75 +69,57 @@ def stop_server(port): else: return False - + @plugin.method('invoiceserver') -def invoiceserver(request, command="start", port=8089): - """Starts a server for requestiong invoices. - +def invoiceserver(request, command="start"): + """Starts a server for requestiong invoices. + A rate limited HTTP Server returns a invoice on the following GET request: - /invoice// - where amount is in Satoshis. - The plugin takes one of the following commands: - {start/stop/restart} and {port} , default port: 8089. + /invoice// + where amount is in Satoshis. + The plugin takes one of the following commands: + {start/stop/status/restart}. """ - commands = {"start", "stop", "restart", "list"} + commands = {"start", "stop", "status","restart"} + port = os.getenv("FLASKPORT", default = 8809) # if command unknown make start our default command if command not in commands: command = "start" - # if port not an integer make 8088 as default - try: - port = int(port) - except: - port = int(plugin.options['invoice-web-port']['value']) - - if command == "list": - return "servers running on the following ports: {}".format(list(jobs.keys())) - if command == "start": if port in jobs: - return "Server already running on port {}. Maybe restart the server?".format(port) - suc = start_server(port) - if suc: - return "started server successfully on port {}".format(port) + return "Invoice server already running on port {}.".format(port) + if start_server(port): + return "Invoice server started successfully on port {}".format(port) else: - return "Could not start server on port {}".format(port) + return "Invoice server could not be started on port {}".format(port) if command == "stop": if stop_server(port): - return "stopped server on port{}".format(port) + return "Invoice server stopped on port {}".format(port) else: - return "could not stop the server" + if port in jobs: + return "Could not stop the Invoice server." + else: + return "Invoice server doen't seem to be active" + + if command == "status": + if port in jobs: + return "Invoice server active on port {}".format(port) + else: + return "Invoice server not active." if command == "restart": stop_server(port) - suc = start_server(port) - if suc: - return "started server successfully on port {}".format(port) - else: - return "Could not start server on port {}".format(port) - -plugin.add_option( - 'invoiceserver-autostart', - 'true', - 'Should the invoice server start automatically' -) - -plugin.add_option( - 'invoiceserver-web-port', - '8809', - 'Which port should the invoice server listen to?' -) - - + start_server(port) + return "Invoice server restarted" @plugin.init() def init(options, configuration, plugin): - port = int(options['invoiceserver-web-port']) + port = os.getenv("FLASKPORT", default = 8809) - if options['invoiceserver-autostart'].lower() in ['true', '1']: + if os.getenv("AUTOSTART") == 1: start_server(port) - plugin.run() diff --git a/request-invoice/requirements.txt b/request-invoice/requirements.txt index fc95529..9815cfc 100644 --- a/request-invoice/requirements.txt +++ b/request-invoice/requirements.txt @@ -1,3 +1,4 @@ +dotenv flask flask-limiter tornado