From be8cfee3c395fedb39a37d3b9f5f0fd778dbc7d3 Mon Sep 17 00:00:00 2001 From: forest Date: Sat, 16 May 2020 21:24:40 -0500 Subject: [PATCH 01/15] add more logs to btcpay webhook --- capsulflask/payment.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/capsulflask/payment.py b/capsulflask/payment.py index 9159d9a..1827d3b 100644 --- a/capsulflask/payment.py +++ b/capsulflask/payment.py @@ -77,11 +77,15 @@ def btcpay_payment(): @bp.route("/btcpay/webhook", methods=("POST",)) def btcpay_webhook(): + current_app.logger.info(f"got btcpay webhook") + # IMPORTANT! there is no signature or credential for the data sent into this webhook :facepalm: # its just a notification, thats all. request_data = json.loads(request.data) invoice_id = request_data['id'] + current_app.logger.info(f"got btcpay webhook with invoice_id={invoice_id}") + # so you better make sure to get the invoice directly from the horses mouth! invoice = current_app.config['BTCPAY_CLIENT'].get_invoice(invoice_id) @@ -90,6 +94,8 @@ def btcpay_webhook(): dollars = invoice['price'] + current_app.logger.info(f"got btcpay webhook with invoice_id={invoice_id}, status={invoice['status']} dollars={dollars}") + if invoice['status'] == "paid" or invoice['status'] == "confirmed" or invoice['status'] == "complete": success_account = get_model().consume_payment_session("btcpay", invoice_id, dollars) From 155b0d579ae10a5af8ec2b9b783e37233a1a0a42 Mon Sep 17 00:00:00 2001 From: forest Date: Sat, 16 May 2020 21:28:28 -0500 Subject: [PATCH 02/15] print invoice --- capsulflask/payment.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/capsulflask/payment.py b/capsulflask/payment.py index 1827d3b..c3eea4f 100644 --- a/capsulflask/payment.py +++ b/capsulflask/payment.py @@ -59,6 +59,9 @@ def btcpay_payment(): redirectURL=f"{current_app.config['BASE_URL']}/console/account-balance", notificationURL=f"{current_app.config['BASE_URL']}/payment/btcpay/webhook" )) + + current_app.logger.info(f"created btcpay invoice: {invoice}") + # print(invoice) invoice_id = invoice["id"] From 2b04463e4e405fc8e710ece1b53f885dfa9fce38 Mon Sep 17 00:00:00 2001 From: forest Date: Sat, 16 May 2020 22:04:51 -0500 Subject: [PATCH 03/15] switch to polling for btcpay payment sessions because btcpay web hooks are super duper slow --- capsulflask/console.py | 7 +++++++ capsulflask/db_model.py | 11 +++++++++++ capsulflask/payment.py | 31 +++++++++++++++++-------------- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/capsulflask/console.py b/capsulflask/console.py index 0b2e89d..2b74875 100644 --- a/capsulflask/console.py +++ b/capsulflask/console.py @@ -15,6 +15,7 @@ from nanoid import generate from capsulflask.metrics import durations as metric_durations from capsulflask.auth import account_required from capsulflask.db import get_model, my_exec_info_message +from capsulflask.payment import poll_btcpay_session from capsulflask import cli bp = Blueprint("console", __name__, url_prefix="/console") @@ -277,6 +278,12 @@ def get_account_balance(vms, payments, as_of): @bp.route("/account-balance") @account_required def account_balance(): + + payment_sessions = get_model().list_payment_sessions_for_account(session['account']) + for payment_session in payment_sessions: + if payment_session['type'] == 'btcpay': + poll_btcpay_session(payment_session['id']) + payments = get_payments() vms = get_vms() balance_1w = get_account_balance(vms, payments, datetime.utcnow() + timedelta(days=7)) diff --git a/capsulflask/db_model.py b/capsulflask/db_model.py index 02a6249..57c86a0 100644 --- a/capsulflask/db_model.py +++ b/capsulflask/db_model.py @@ -164,6 +164,17 @@ class DBModel: ) self.connection.commit() + def list_payment_sessions_for_account(self, email): + self.cursor.execute(""" + SELECT id, type, dollars, created + FROM payment_sessions WHERE email = %s""", + (email, ) + ) + return list(map( + lambda x: dict(id=x[0], type=x[1], dollars=x[2], created=x[3]), + self.cursor.fetchall() + )) + def consume_payment_session(self, payment_type, id, dollars): self.cursor.execute("SELECT email, dollars FROM payment_sessions WHERE id = %s AND type = %s", (id, payment_type)) row = self.cursor.fetchone() diff --git a/capsulflask/payment.py b/capsulflask/payment.py index c3eea4f..8bbb433 100644 --- a/capsulflask/payment.py +++ b/capsulflask/payment.py @@ -77,23 +77,12 @@ def btcpay_payment(): return render_template("btcpay.html", invoice_id=invoice_id) -@bp.route("/btcpay/webhook", methods=("POST",)) -def btcpay_webhook(): - - current_app.logger.info(f"got btcpay webhook") - - # IMPORTANT! there is no signature or credential for the data sent into this webhook :facepalm: - # its just a notification, thats all. - request_data = json.loads(request.data) - invoice_id = request_data['id'] - - current_app.logger.info(f"got btcpay webhook with invoice_id={invoice_id}") - +def poll_btcpay_session(invoice_id): # so you better make sure to get the invoice directly from the horses mouth! invoice = current_app.config['BTCPAY_CLIENT'].get_invoice(invoice_id) if invoice['currency'] != "USD": - abort(400, "invalid currency") + return [400, "invalid currency"] dollars = invoice['price'] @@ -110,7 +99,21 @@ def btcpay_webhook(): elif invoice['status'] == "expired" or invoice['status'] == "invalid": get_model().btcpay_invoice_resolved(invoice_id, False) - return {"msg": "ok"}, 200 + return [200, "ok"] + +@bp.route("/btcpay/webhook", methods=("POST",)) +def btcpay_webhook(): + + current_app.logger.info(f"got btcpay webhook") + + # IMPORTANT! there is no signature or credential for the data sent into this webhook :facepalm: + # its just a notification, thats all. + request_data = json.loads(request.data) + invoice_id = request_data['id'] + + result = poll_btcpay_session(invoice_id) + + abort(result[0], result[1]) From 27c7f28e53b48decb6ca3c06215beffc622b7c86 Mon Sep 17 00:00:00 2001 From: forest Date: Sat, 16 May 2020 22:08:59 -0500 Subject: [PATCH 04/15] remove test data --- .../schema_migrations/02_up_accounts_vms_etc.sql | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/capsulflask/schema_migrations/02_up_accounts_vms_etc.sql b/capsulflask/schema_migrations/02_up_accounts_vms_etc.sql index 7f98356..87e8bf8 100644 --- a/capsulflask/schema_migrations/02_up_accounts_vms_etc.sql +++ b/capsulflask/schema_migrations/02_up_accounts_vms_etc.sql @@ -98,14 +98,4 @@ VALUES ('f1-s', 5.33, 512, 1, 500), ('f1-xx', 29.66, 8192, 4, 8000), ('f1-xxx', 57.58, 16384, 8, 16000); --- this is test data to be removed later -INSERT INTO accounts (email) -VALUES ('forest.n.johnson@gmail.com'); - -INSERT INTO payments (email, dollars, created) -VALUES ('forest.n.johnson@gmail.com', 20.00, TO_TIMESTAMP('2020-04-05','YYYY-MM-DD')); - -INSERT INTO vms (id, email, os, size, created) -VALUES ('capsul-yi9ffqbjly', 'forest.n.johnson@gmail.com', 'alpine311', 'f1-xx', TO_TIMESTAMP('2020-04-19','YYYY-MM-DD')); - UPDATE schemaversion SET version = 2; \ No newline at end of file From 321a46d77b3a20956accd2ed13982174a1188214 Mon Sep 17 00:00:00 2001 From: forest Date: Sat, 16 May 2020 22:11:44 -0500 Subject: [PATCH 05/15] add ecdsa to the pipfile --- Pipfile | 1 + Pipfile.lock | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Pipfile b/Pipfile index e0edea9..40866b6 100644 --- a/Pipfile +++ b/Pipfile @@ -28,6 +28,7 @@ stripe = "*" matplotlib = "*" requests = "*" python-dotenv = "*" +ecdsa = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 25156ce..e17b127 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "4f61804f9405ff8f66e41739aeac24a2577f7b64f7d08b93459b568528c4f494" + "sha256": "89f5b91fef8e0029cbecf7f83ce1fdc64a64f295161c8c63bfe5940e31d466e1" }, "pipfile-spec": 6, "requires": { @@ -60,6 +60,14 @@ ], "version": "==0.10.0" }, + "ecdsa": { + "hashes": [ + "sha256:867ec9cf6df0b03addc8ef66b56359643cb5d0c1dc329df76ba7ecfe256c8061", + "sha256:8f12ac317f8a1318efa75757ef0a651abe12e51fc1af8838fb91079445227277" + ], + "index": "pypi", + "version": "==0.15" + }, "flask": { "hashes": [ "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060", From e3a2e8eee4cca206a964aed16766a2085b046bba Mon Sep 17 00:00:00 2001 From: forest Date: Sat, 16 May 2020 23:02:02 -0500 Subject: [PATCH 06/15] fine-tuning the create-capsul experience --- capsulflask/console.py | 12 +++++--- capsulflask/static/style.css | 11 +++++++ capsulflask/templates/base.html | 1 + capsulflask/templates/capsuls.html | 6 ++++ capsulflask/templates/create-capsul.html | 37 ++++++++++++------------ 5 files changed, 44 insertions(+), 23 deletions(-) diff --git a/capsulflask/console.py b/capsulflask/console.py index 2b74875..59797c0 100644 --- a/capsulflask/console.py +++ b/capsulflask/console.py @@ -8,6 +8,8 @@ from flask import g from flask import request from flask import session from flask import render_template +from flask import redirect +from flask import url_for from flask_mail import Message from werkzeug.exceptions import abort from nanoid import generate @@ -42,6 +44,9 @@ def double_check_capsul_address(id, ipv4): @account_required def index(): vms = get_vms() + created = request.args.get('created') + if not re.match(r"^(cvm|capsul)-[a-z0-9]{10}$", created): + created = '___________' # for now we are going to check the IP according to the virt model # on every request. this could be done by a background job and cached later on... @@ -60,7 +65,7 @@ def index(): list(filter(lambda x: not x['deleted'], vms)) )) - return render_template("capsuls.html", vms=vms, has_vms=len(vms) > 0) + return render_template("capsuls.html", vms=vms, has_vms=len(vms) > 0, created=created) @bp.route("/", methods=("GET", "POST")) @account_required @@ -111,7 +116,6 @@ def create(): account_balance = get_account_balance(get_vms(), get_payments(), datetime.utcnow()) capacity_avaliable = current_app.config["VIRTUALIZATION_MODEL"].capacity_avaliable(512*1024*1024) errors = list() - created_os = None if request.method == "POST": @@ -172,7 +176,8 @@ def create(): memory_mb=vm_sizes[size]['memory_mb'], ssh_public_keys=list(map(lambda x: x["content"], posted_keys)) ) - created_os = os + + return redirect(f"{url_for('console.index')}?created={id}") affordable_vm_sizes = dict() for key, vm_size in vm_sizes.items(): @@ -187,7 +192,6 @@ def create(): return render_template( "create-capsul.html", - created_os=created_os, capacity_avaliable=capacity_avaliable, account_balance=format(account_balance, '.2f'), ssh_public_keys=ssh_public_keys, diff --git a/capsulflask/static/style.css b/capsulflask/static/style.css index e496808..8353e1c 100644 --- a/capsulflask/static/style.css +++ b/capsulflask/static/style.css @@ -45,6 +45,15 @@ a:hover, a:active, a:visited { padding: 1em; } +.flash.green { + color: rgb(8, 173, 137); + border-color: rgb(8, 173, 137); +} + +.display-none { + display: none; +} + h1, h2, h3, h4, h5 { font-size:calc(0.40rem + 1vmin); margin: initial; @@ -63,6 +72,8 @@ main { align-items: center; } + + .full-margin { width: 100%; margin: 3rem 0; diff --git a/capsulflask/templates/base.html b/capsulflask/templates/base.html index 68408ba..21b4af1 100644 --- a/capsulflask/templates/base.html +++ b/capsulflask/templates/base.html @@ -39,6 +39,7 @@ {% for message in get_flashed_messages() %}
{{ message }}
{% endfor %} +{% block custom_flash %}{% endblock %}
{% block content %}{% endblock %}
diff --git a/capsulflask/templates/capsuls.html b/capsulflask/templates/capsuls.html index ee65a5c..0122089 100644 --- a/capsulflask/templates/capsuls.html +++ b/capsulflask/templates/capsuls.html @@ -2,6 +2,12 @@ {% block title %}Capsuls{% endblock %} +{% block custom_flash %} +{% if created %} +
{{ created }} successfully created!
+{% endif %} +{% endblock %} + {% block content %}

Capsuls

diff --git a/capsulflask/templates/create-capsul.html b/capsulflask/templates/create-capsul.html index 9e05ae4..d9a24fd 100644 --- a/capsulflask/templates/create-capsul.html +++ b/capsulflask/templates/create-capsul.html @@ -6,21 +6,10 @@

Create Capsul

-
- -{% if created_os %} -

- Your Capsul was successfully created! You should already see it listed on the - Capsuls page, but it may not have obtained an IP address yet. - Its IP address should become visible once the machine has booted and taken a DHCP lease. -

- {% if created_os == 'debian10' %} -

- Note: because Debian delays fully booting until after entropy has been generated, Debian Capsuls - may take an extra-long time to obtain an IP address, like up to 10 minutes. Be patient. -

- {% endif %} -{% else %} +
+
+ + {% if cant_afford %}

Your account does not have sufficient funds to create a Capsul. @@ -79,14 +68,24 @@

- + +
+ -
{% endif %} -{% endif %} -
+ + {% endblock %} {% block subcontent %} From 5c1a977a874500315637f75ba7923a1ea2994bcf Mon Sep 17 00:00:00 2001 From: forest Date: Sat, 16 May 2020 23:05:45 -0500 Subject: [PATCH 07/15] remove query string XSS from login token --- capsulflask/auth.py | 4 ++++ capsulflask/console.py | 2 ++ 2 files changed, 6 insertions(+) diff --git a/capsulflask/auth.py b/capsulflask/auth.py index cda0942..53e31a6 100644 --- a/capsulflask/auth.py +++ b/capsulflask/auth.py @@ -71,6 +71,10 @@ def magiclink(token): session["account"] = email return redirect(url_for("console.index")) else: + # this is here to prevent xss + if not re.match(r"^[a-zA-Z0-9_-]+$", token): + token = '___________' + abort(404, f"Token {token} doesn't exist or has already been used.") @bp.route("/logout") diff --git a/capsulflask/console.py b/capsulflask/console.py index 59797c0..5038ffb 100644 --- a/capsulflask/console.py +++ b/capsulflask/console.py @@ -45,6 +45,8 @@ def double_check_capsul_address(id, ipv4): def index(): vms = get_vms() created = request.args.get('created') + + # this is here to prevent xss if not re.match(r"^(cvm|capsul)-[a-z0-9]{10}$", created): created = '___________' From 4f002458b30156669e766222976ae729944e2e70 Mon Sep 17 00:00:00 2001 From: forest Date: Sat, 16 May 2020 23:11:47 -0500 Subject: [PATCH 08/15] adjust memory metric query --- capsulflask/metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capsulflask/metrics.py b/capsulflask/metrics.py index 1e71b0a..b09c590 100644 --- a/capsulflask/metrics.py +++ b/capsulflask/metrics.py @@ -86,7 +86,7 @@ def get_plot_bytes(metric, capsulid, duration, size): # Prometheus queries to pull metrics for VMs metric_queries = dict( cpu=f"irate(libvirtd_domain_info_cpu_time_seconds_total{{domain='{capsulid}'}}[30s])", - memory=f"libvirtd_domain_info_memory_usage_bytes{{domain='{capsulid}'}}", + memory=f"libvirtd_domain_info_memory_max_bytes{{domain='{capsulid}'}}-libvirtd_domain_info_memory_unused_bytes{{domain='{capsulid}'}}", network_in=f"rate(libvirtd_domain_interface_stats_receive_bytes_total{{domain='{capsulid}'}}[{interval_seconds}s])", network_out=f"rate(libvirtd_domain_interface_stats_transmit_bytes_total{{domain='{capsulid}'}}[{interval_seconds}s])", disk=f"rate(libvirtd_domain_block_stats_read_bytes_total{{domain='{capsulid}'}}[{interval_seconds}s])%2Brate(libvirtd_domain_block_stats_write_bytes_total{{domain='{capsulid}'}}[{interval_seconds}s])", From 4b169e98863a95d6ae98f0c3155f14f2102d2f8a Mon Sep 17 00:00:00 2001 From: forest Date: Sat, 16 May 2020 23:21:09 -0500 Subject: [PATCH 09/15] implement bill for at least 24 hours --- capsulflask/console.py | 7 +++++-- capsulflask/templates/create-capsul.html | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/capsulflask/console.py b/capsulflask/console.py index 5038ffb..ca1e6c9 100644 --- a/capsulflask/console.py +++ b/capsulflask/console.py @@ -45,7 +45,7 @@ def double_check_capsul_address(id, ipv4): def index(): vms = get_vms() created = request.args.get('created') - + # this is here to prevent xss if not re.match(r"^(cvm|capsul)-[a-z0-9]{10}$", created): created = '___________' @@ -274,7 +274,10 @@ def get_account_balance(vms, payments, as_of): vm_cost_dollars = 0.0 for vm in vms: end_datetime = vm["deleted"] if vm["deleted"] else as_of - vm_months = ( end_datetime - vm["created"] ).days / average_number_of_days_in_a_month + days = float((end_datetime - vm["created"]).total_seconds())/float(60*60*24) + if days < 1: + days = float(1) + vm_months = days / average_number_of_days_in_a_month vm_cost_dollars += vm_months * float(vm["dollars_per_month"]) payment_dollars_total = float( sum(map(lambda x: 0 if x["invalidated"] else x["dollars"], payments)) ) diff --git a/capsulflask/templates/create-capsul.html b/capsulflask/templates/create-capsul.html index d9a24fd..bb27042 100644 --- a/capsulflask/templates/create-capsul.html +++ b/capsulflask/templates/create-capsul.html @@ -25,7 +25,7 @@
       CAPSUL SIZES
       ============
-      type     monthly   cpus  mem     ssd   net*
+      type     monthly*  cpus  mem     ssd   net*
       -----    -------   ----  ---     ---   --- 
       f1-s     $5.33     1     512M    25G   .5TB
       f1-m     $7.16     1     1024M   25G   1TB 
@@ -35,6 +35,7 @@
       f1-xxx   $57.58    8     16G     25G   16TB
       
       * net is calculated as a per-month average
+      * vms are billed for a minimum of 24 hours upon creation
       * all VMs come standard with one public IPv4 addr
       Your account balance: ${{ account_balance }}

From 684128361ca23a4acfa8a9af99b102148149ae7d Mon Sep 17 00:00:00 2001
From: forest 
Date: Sat, 16 May 2020 23:24:11 -0500
Subject: [PATCH 10/15] correctly display vm_months

---
 capsulflask/console.py | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/capsulflask/console.py b/capsulflask/console.py
index ca1e6c9..65e2c16 100644
--- a/capsulflask/console.py
+++ b/capsulflask/console.py
@@ -269,15 +269,18 @@ def get_payments():
 
 average_number_of_days_in_a_month = 30.44
 
+def get_vm_months_float(vm, as_of):
+  end_datetime = vm["deleted"] if vm["deleted"] else as_of
+  days = float((end_datetime - vm["created"]).total_seconds())/float(60*60*24)
+  if days < 1:
+    days = float(1)
+  return days / average_number_of_days_in_a_month
+
 def get_account_balance(vms, payments, as_of):
 
   vm_cost_dollars = 0.0
   for vm in vms:
-    end_datetime = vm["deleted"] if vm["deleted"] else as_of
-    days = float((end_datetime - vm["created"]).total_seconds())/float(60*60*24)
-    if days < 1:
-      days = float(1)
-    vm_months = days / average_number_of_days_in_a_month
+    vm_months = get_vm_months_float(vm, as_of)
     vm_cost_dollars += vm_months * float(vm["dollars_per_month"])
 
   payment_dollars_total = float( sum(map(lambda x: 0 if x["invalidated"] else x["dollars"], payments)) )
@@ -314,9 +317,7 @@ def account_balance():
   vms_billed = list()
 
   for vm in get_vms():
-    end_datetime = vm["deleted"] if vm["deleted"] else datetime.utcnow()
-
-    vm_months = (end_datetime - vm["created"]).days / average_number_of_days_in_a_month
+    vm_months = get_vm_months_float(vm, datetime.utcnow())
     vms_billed.append(dict(
       id=vm["id"], 
       dollars_per_month=vm["dollars_per_month"],

From 62a062fff2091f264d8d9b34e33b448e3b49f852 Mon Sep 17 00:00:00 2001
From: forest 
Date: Sat, 16 May 2020 23:28:21 -0500
Subject: [PATCH 11/15] fix bug when created query string is not present

---
 capsulflask/auth.py    | 2 +-
 capsulflask/console.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/capsulflask/auth.py b/capsulflask/auth.py
index 53e31a6..5509702 100644
--- a/capsulflask/auth.py
+++ b/capsulflask/auth.py
@@ -72,7 +72,7 @@ def magiclink(token):
         return redirect(url_for("console.index"))
     else:
         # this is here to prevent xss
-        if not re.match(r"^[a-zA-Z0-9_-]+$", token):
+        if token and not re.match(r"^[a-zA-Z0-9_-]+$", token):
           token = '___________'
 
         abort(404, f"Token {token} doesn't exist or has already been used.")
diff --git a/capsulflask/console.py b/capsulflask/console.py
index 65e2c16..d827b4f 100644
--- a/capsulflask/console.py
+++ b/capsulflask/console.py
@@ -47,7 +47,7 @@ def index():
   created = request.args.get('created')
 
   # this is here to prevent xss
-  if not re.match(r"^(cvm|capsul)-[a-z0-9]{10}$", created):
+  if created and not re.match(r"^(cvm|capsul)-[a-z0-9]{10}$", created):
     created = '___________'
 
   # for now we are going to check the IP according to the virt model

From bb90738fdeb5d8ac4ed11151e12a4297d98dcbd7 Mon Sep 17 00:00:00 2001
From: forest 
Date: Sat, 16 May 2020 23:44:03 -0500
Subject: [PATCH 12/15] libvirtd_domain_info_maximum_memory_bytes

---
 capsulflask/metrics.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/capsulflask/metrics.py b/capsulflask/metrics.py
index b09c590..fc33b61 100644
--- a/capsulflask/metrics.py
+++ b/capsulflask/metrics.py
@@ -86,7 +86,7 @@ def get_plot_bytes(metric, capsulid, duration, size):
   # Prometheus queries to pull metrics for VMs
   metric_queries = dict(
     cpu=f"irate(libvirtd_domain_info_cpu_time_seconds_total{{domain='{capsulid}'}}[30s])",
-    memory=f"libvirtd_domain_info_memory_max_bytes{{domain='{capsulid}'}}-libvirtd_domain_info_memory_unused_bytes{{domain='{capsulid}'}}",
+    memory=f"libvirtd_domain_info_maximum_memory_bytes{{domain='{capsulid}'}}-libvirtd_domain_info_memory_unused_bytes{{domain='{capsulid}'}}",
     network_in=f"rate(libvirtd_domain_interface_stats_receive_bytes_total{{domain='{capsulid}'}}[{interval_seconds}s])",
     network_out=f"rate(libvirtd_domain_interface_stats_transmit_bytes_total{{domain='{capsulid}'}}[{interval_seconds}s])",
     disk=f"rate(libvirtd_domain_block_stats_read_bytes_total{{domain='{capsulid}'}}[{interval_seconds}s])%2Brate(libvirtd_domain_block_stats_write_bytes_total{{domain='{capsulid}'}}[{interval_seconds}s])",

From 633e2907dfe196292be7d782f0b7d2f5d51eec4c Mon Sep 17 00:00:00 2001
From: forest 
Date: Sat, 16 May 2020 23:50:39 -0500
Subject: [PATCH 13/15] changelog: 2020-05-16: Beta version of new Capsul web
 application

---
 capsulflask/templates/changelog.html | 1 +
 1 file changed, 1 insertion(+)

diff --git a/capsulflask/templates/changelog.html b/capsulflask/templates/changelog.html
index 89deb19..685bc5d 100644
--- a/capsulflask/templates/changelog.html
+++ b/capsulflask/templates/changelog.html
@@ -19,6 +19,7 @@
    
  • 2020-04-17: OpenBSD support added
  • 2020-04-26: Support link added
  • 2020-05-04: Simplified payment page
  • +
  • 2020-05-16: Beta version of new Capsul web application
  • {% endblock %} From 496db2bb06c7bd3ccad6e10c30a5772289e72196 Mon Sep 17 00:00:00 2001 From: forest Date: Sun, 17 May 2020 00:00:36 -0500 Subject: [PATCH 14/15] add new tables to 02_down migration script --- capsulflask/schema_migrations/02_down_accounts_vms_etc.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/capsulflask/schema_migrations/02_down_accounts_vms_etc.sql b/capsulflask/schema_migrations/02_down_accounts_vms_etc.sql index 195be9b..9910c89 100644 --- a/capsulflask/schema_migrations/02_down_accounts_vms_etc.sql +++ b/capsulflask/schema_migrations/02_down_accounts_vms_etc.sql @@ -1,3 +1,7 @@ +DROP TABLE unresolved_btcpay_invoices; + +DROP TABLE payment_sessions; + DROP TABLE payments; DROP TABLE login_tokens; From 17c559123cb32c98b87da2528921882c955f4613 Mon Sep 17 00:00:00 2001 From: forest Date: Sun, 17 May 2020 00:04:15 -0500 Subject: [PATCH 15/15] readme explain \n in BTCPAY_PRIVATE_KEY --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f51317c..4c59d52 100644 --- a/README.md +++ b/README.md @@ -152,10 +152,8 @@ And you should see the token in the btcpay server UI: Now simply set your `BTCPAY_PRIVATE_KEY` variable in `.env` +NOTE: make sure to use single quotes and replace the new lines with \n. + ``` -BTCPAY_PRIVATE_KEY='-----BEGIN EC PRIVATE KEY----- -EXAMPLEIArx/EXAMPLEKH23EXAMPLEsYXEXAMPLE5qdEXAMPLEcFHoAcEXAMPLEK -oUQDQgAEnWs47PT8+ihhzyvXX6/yYMAWWODluRTR2Ix6ZY7Z+MV7v0W1maJzqeqq -NQ+cpBvPDbyrDk9+Uf/sEaRCma094g== ------END EC PRIVATE KEY-----' +BTCPAY_PRIVATE_KEY='-----BEGIN EC PRIVATE KEY-----\nEXAMPLEIArx/EXAMPLEKH23EXAMPLEsYXEXAMPLE5qdEXAMPLEcFHoAcEXAMPLEK\noUQDQgAEnWs47PT8+ihhzyvXX6/yYMAWWODluRTR2Ix6ZY7Z+MV7v0W1maJzqeqq\nNQ+cpBvPDbyrDk9+Uf/sEaRCma094g==\n-----END EC PRIVATE KEY-----' ``` \ No newline at end of file