+27
-2
capsulflask/__init__.py
+27
-2
capsulflask/__init__.py
···
1
+
import logging
2
+
from logging.config import dictConfig as logging_dict_config
1
3
2
4
import os
3
5
···
13
15
load_dotenv(find_dotenv())
14
16
15
17
app = Flask(__name__)
18
+
16
19
app.config.from_mapping(
17
20
BASE_URL=os.environ.get("BASE_URL", default="http://localhost:5000"),
18
21
SECRET_KEY=os.environ.get("SECRET_KEY", default="dev"),
22
+
LOG_LEVEL=os.environ.get("LOG_LEVEL", default="INFO"),
23
+
19
24
DATABASE_URL=os.environ.get("DATABASE_URL", default="sql://postgres:dev@localhost:5432/postgres"),
20
25
DATABASE_SCHEMA=os.environ.get("DATABASE_SCHEMA", default="public"),
21
26
···
37
42
BTCPAY_URL=os.environ.get("BTCPAY_URL", default="https://btcpay.cyberia.club")
38
43
)
39
44
45
+
logging_dict_config({
46
+
'version': 1,
47
+
'formatters': {'default': {
48
+
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
49
+
}},
50
+
'handlers': {'wsgi': {
51
+
'class': 'logging.StreamHandler',
52
+
'stream': 'ext://flask.logging.wsgi_errors_stream',
53
+
'formatter': 'default'
54
+
}},
55
+
'root': {
56
+
'level': app.config['LOG_LEVEL'],
57
+
'handlers': ['wsgi']
58
+
}
59
+
})
60
+
61
+
# app.logger.critical("critical")
62
+
# app.logger.error("error")
63
+
# app.logger.warning("warning")
64
+
# app.logger.info("info")
65
+
# app.logger.debug("debug")
66
+
40
67
stripe.api_key = app.config['STRIPE_SECRET_KEY']
41
68
stripe.api_version = app.config['STRIPE_API_VERSION']
42
69
···
59
86
60
87
app.add_url_rule("/", endpoint="index")
61
88
62
-
if __name__ == "__main__":
63
-
app.run(host='127.0.0.1')
+10
-10
capsulflask/cli.py
+10
-10
capsulflask/cli.py
···
66
66
def cron_task():
67
67
68
68
# make sure btcpay payments get completed (in case we miss a webhook), otherwise invalidate the payment
69
-
print("cron_task: starting clean_up_unresolved_btcpay_invoices")
69
+
current_app.logger.info("cron_task: starting clean_up_unresolved_btcpay_invoices")
70
70
clean_up_unresolved_btcpay_invoices()
71
-
print("cron_task: finished clean_up_unresolved_btcpay_invoices")
71
+
current_app.logger.info("cron_task: finished clean_up_unresolved_btcpay_invoices")
72
72
73
73
# notify when funds run out
74
-
print("cron_task: starting notify_users_about_account_balance")
74
+
current_app.logger.info("cron_task: starting notify_users_about_account_balance")
75
75
notify_users_about_account_balance()
76
-
print("cron_task: finished notify_users_about_account_balance")
76
+
current_app.logger.info("cron_task: finished notify_users_about_account_balance")
77
77
78
78
# make sure vm system and DB are synced
79
-
print("cron_task: starting ensure_vms_and_db_are_synced")
79
+
current_app.logger.info("cron_task: starting ensure_vms_and_db_are_synced")
80
80
ensure_vms_and_db_are_synced()
81
-
print("cron_task: finished ensure_vms_and_db_are_synced")
81
+
current_app.logger.info("cron_task: finished ensure_vms_and_db_are_synced")
82
82
83
83
84
84
···
90
90
days = float((datetime.now() - unresolved_invoice['created']).total_seconds())/float(60*60*24)
91
91
92
92
if btcpay_invoice['status'] == "complete":
93
-
print(
93
+
current_app.logger.info(
94
94
f"resolving btcpay invoice {invoice_id} "
95
95
f"({unresolved_invoice['email']}, ${unresolved_invoice['dollars']}) as completed "
96
96
)
97
97
get_model().btcpay_invoice_resolved(invoice_id, True)
98
98
elif days >= 1:
99
-
print(
99
+
current_app.logger.info(
100
100
f"resolving btcpay invoice {invoice_id} "
101
101
f"({unresolved_invoice['email']}, ${unresolved_invoice['dollars']}) as invalidated, "
102
102
f"btcpay server invoice status: {btcpay_invoice['status']}"
···
224
224
index_to_send = i
225
225
226
226
if index_to_send > -1:
227
-
print(f"cron_task: sending {warnings[index_to_send]['id']} warning email to {account['email']}.")
227
+
current_app.logger.info(f"cron_task: sending {warnings[index_to_send]['id']} warning email to {account['email']}.")
228
228
get_body = warnings[index_to_send]['get_body']
229
229
get_subject = warnings[index_to_send]['get_subject']
230
230
current_app.config["FLASK_MAIL_INSTANCE"].send(
···
237
237
get_model().set_account_balance_warning(account['email'], warnings[index_to_send]['id'])
238
238
if index_to_send == len(warnings)-1:
239
239
for vm in vms:
240
-
print(f"cron_task: deleting {vm['id']} ( {account['email']} ) due to negative account balance.")
240
+
current_app.logger.warning(f"cron_task: deleting {vm['id']} ( {account['email']} ) due to negative account balance.")
241
241
current_app.config["VIRTUALIZATION_MODEL"].destroy(email=account["email"], id=vm['id'])
242
242
get_model().delete_vm(email=account["email"], id=vm['id'])
243
243
+4
-5
capsulflask/console.py
+4
-5
capsulflask/console.py
···
30
30
ipv4 = result.ipv4
31
31
get_model().update_vm_ip(email=session["account"], id=id, ipv4=result.ipv4)
32
32
except:
33
-
print(f"""
33
+
current_app.logger.error(f"""
34
34
the virtualization model threw an error in double_check_capsul_address of {id}:
35
35
{my_exec_info_message(sys.exc_info())}"""
36
36
)
···
82
82
83
83
return render_template("capsul-detail.html", vm=vm, delete=True, deleted=False)
84
84
else:
85
-
print(f"deleting {vm['id']} per user request ({session['account']})")
85
+
current_app.logger.info(f"deleting {vm['id']} per user request ({session['account']})")
86
86
current_app.config["VIRTUALIZATION_MODEL"].destroy(email=session['account'], id=id)
87
87
get_model().delete_vm(email=session['account'], id=id)
88
88
···
182
182
flash(error)
183
183
184
184
if not capacity_avaliable:
185
-
print(f"when capsul capacity is restored, send an email to {session['account']}")
185
+
current_app.logger.warning(f"when capsul capacity is restored, send an email to {session['account']}")
186
186
187
187
return render_template(
188
188
"create-capsul.html",
···
299
299
300
300
for vm in get_vms():
301
301
end_datetime = vm["deleted"] if vm["deleted"] else datetime.utcnow()
302
-
# print(end_datetime)
303
-
# print(vm["created"])
302
+
304
303
vm_months = (end_datetime - vm["created"]).days / average_number_of_days_in_a_month
305
304
vms_billed.append(dict(
306
305
id=vm["id"],
+10
-10
capsulflask/db.py
+10
-10
capsulflask/db.py
···
25
25
26
26
schemaMigrations = {}
27
27
schemaMigrationsPath = join(app.root_path, 'schema_migrations')
28
-
print("loading schema migration scripts from {}".format(schemaMigrationsPath))
28
+
app.logger.info("loading schema migration scripts from {}".format(schemaMigrationsPath))
29
29
for filename in listdir(schemaMigrationsPath):
30
30
result = re.search(r"^\d+_(up|down)", filename)
31
31
if not result:
32
-
print(f"schemaVersion {filename} must match ^\d+_(up|down). exiting.")
32
+
app.logger.error(f"schemaVersion {filename} must match ^\\d+_(up|down). exiting.")
33
33
exit(1)
34
34
key = result.group()
35
35
with open(join(schemaMigrationsPath, filename), 'rb') as file:
···
54
54
hasSchemaVersionTable = True
55
55
56
56
if hasSchemaVersionTable == False:
57
-
print("no table named schemaversion found in the {} schema. running migration 01_up".format(app.config['DATABASE_SCHEMA']))
57
+
app.logger.info("no table named schemaversion found in the {} schema. running migration 01_up".format(app.config['DATABASE_SCHEMA']))
58
58
try:
59
59
cursor.execute(schemaMigrations["01_up"])
60
60
connection.commit()
61
61
except:
62
-
print("unable to create the schemaversion table because: {}".format(my_exec_info_message(sys.exc_info())))
62
+
app.logger.error("unable to create the schemaversion table because: {}".format(my_exec_info_message(sys.exc_info())))
63
63
exit(1)
64
64
actionWasTaken = True
65
65
···
67
67
schemaVersion = cursor.fetchall()[0][0]
68
68
69
69
if schemaVersion > desiredSchemaVersion:
70
-
print("schemaVersion ({}) > desiredSchemaVersion ({}). schema downgrades are not supported yet. exiting.".format(
70
+
app.logger.critical("schemaVersion ({}) > desiredSchemaVersion ({}). schema downgrades are not supported yet. exiting.".format(
71
71
schemaVersion, desiredSchemaVersion
72
72
))
73
73
exit(1)
74
74
75
75
while schemaVersion < desiredSchemaVersion:
76
76
migrationKey = "%02d_up" % (schemaVersion+1)
77
-
print("schemaVersion ({}) < desiredSchemaVersion ({}). running migration {}".format(
77
+
app.logger.info("schemaVersion ({}) < desiredSchemaVersion ({}). running migration {}".format(
78
78
schemaVersion, desiredSchemaVersion, migrationKey
79
79
))
80
80
try:
81
81
cursor.execute(schemaMigrations[migrationKey])
82
82
connection.commit()
83
83
except KeyError:
84
-
print("missing schema migration script: {}_xyz.sql".format(migrationKey))
84
+
app.logger.critical("missing schema migration script: {}_xyz.sql".format(migrationKey))
85
85
exit(1)
86
86
except:
87
-
print("unable to execute the schema migration {} because: {}".format(migrationKey, my_exec_info_message(sys.exc_info())))
87
+
app.logger.critical("unable to execute the schema migration {} because: {}".format(migrationKey, my_exec_info_message(sys.exc_info())))
88
88
exit(1)
89
89
actionWasTaken = True
90
90
···
93
93
versionFromDatabase = cursor.fetchall()[0][0]
94
94
95
95
if schemaVersion != versionFromDatabase:
96
-
print("incorrect schema version value \"{}\" after running migration {}, expected \"{}\". exiting.".format(
96
+
app.logger.critical("incorrect schema version value \"{}\" after running migration {}, expected \"{}\". exiting.".format(
97
97
versionFromDatabase,
98
98
migrationKey,
99
99
schemaVersion
···
104
104
105
105
app.config['PSYCOPG2_CONNECTION_POOL'].putconn(connection)
106
106
107
-
print("{} current schemaVersion: \"{}\"".format(
107
+
app.logger.info("{} current schemaVersion: \"{}\"".format(
108
108
("schema migration completed." if actionWasTaken else "schema is already up to date. "), schemaVersion
109
109
))
110
110
+2
-1
capsulflask/db_model.py
+2
-1
capsulflask/db_model.py
···
1
1
2
2
from nanoid import generate
3
+
from flask import current_app
3
4
4
5
5
6
class DBModel:
···
168
169
row = self.cursor.fetchone()
169
170
if row:
170
171
if int(row[1]) != int(dollars):
171
-
print(f"""
172
+
current_app.logger.warning(f"""
172
173
{payment_type} gave us a completed payment session with a different dollar amount than what we had recorded!!
173
174
id: {id}
174
175
account: {row[0]}
+1
capsulflask/landing.py
+1
capsulflask/landing.py
+6
-6
capsulflask/payment.py
+6
-6
capsulflask/payment.py
···
62
62
# print(invoice)
63
63
invoice_id = invoice["id"]
64
64
65
-
print(f"created btcpay invoice_id={invoice_id} ( {session['account']}, ${dollars} )")
65
+
current_app.logger.info(f"created btcpay invoice_id={invoice_id} ( {session['account']}, ${dollars} )")
66
66
67
67
get_model().create_payment_session("btcpay", invoice_id, session["account"], dollars)
68
68
···
94
94
success_account = get_model().consume_payment_session("btcpay", invoice_id, dollars)
95
95
96
96
if success_account:
97
-
print(f"{success_account} paid ${dollars} successfully (btcpay_invoice_id={invoice_id})")
97
+
current_app.logger.info(f"{success_account} paid ${dollars} successfully (btcpay_invoice_id={invoice_id})")
98
98
99
99
if invoice['status'] == "complete":
100
100
get_model().btcpay_invoice_resolved(invoice_id, True)
···
119
119
120
120
if len(errors) == 0:
121
121
122
-
print(f"creating stripe checkout session for {session['account']}, ${dollars}")
122
+
current_app.logger.info(f"creating stripe checkout session for {session['account']}, ${dollars}")
123
123
124
124
checkout_session = stripe.checkout.Session.create(
125
125
success_url=current_app.config['BASE_URL'] + "/payment/stripe/success?session_id={CHECKOUT_SESSION_ID}",
···
138
138
)
139
139
stripe_checkout_session_id = checkout_session['id']
140
140
141
-
print(f"stripe_checkout_session_id={stripe_checkout_session_id} ( {session['account']}, ${dollars} )")
141
+
current_app.logger.info(f"stripe_checkout_session_id={stripe_checkout_session_id} ( {session['account']}, ${dollars} )")
142
142
143
143
get_model().create_payment_session("stripe", stripe_checkout_session_id, session["account"], dollars)
144
144
···
191
191
def success():
192
192
stripe_checkout_session_id = request.args.get('session_id')
193
193
if not stripe_checkout_session_id:
194
-
print("/payment/stripe/success returned 400: missing required URL parameter session_id")
194
+
current_app.logger.info("/payment/stripe/success returned 400: missing required URL parameter session_id")
195
195
abort(400, "missing required URL parameter session_id")
196
196
else:
197
197
for _ in range(0, 5):
198
198
paid = validate_stripe_checkout_session(stripe_checkout_session_id)
199
199
if paid:
200
-
print(f"{paid['email']} paid ${paid['dollars']} successfully (stripe_checkout_session_id={stripe_checkout_session_id})")
200
+
current_app.logger.info(f"{paid['email']} paid ${paid['dollars']} successfully (stripe_checkout_session_id={stripe_checkout_session_id})")
201
201
return redirect(url_for("console.account_balance"))
202
202
else:
203
203
sleep(1)
+4
-4
capsulflask/virt_model.py
+4
-4
capsulflask/virt_model.py
···
47
47
48
48
def create(self, email: str, id: str, template_image_file_name: str, vcpus: int, memory_mb: int, ssh_public_keys: list):
49
49
validate_capsul_id(id)
50
-
print(f"mock create: {id} for {email}")
50
+
current_app.logger.info(f"mock create: {id} for {email}")
51
51
sleep(1)
52
52
53
53
def destroy(self, email: str, id: str):
54
-
print(f"mock destroy: {id} for {email}")
54
+
current_app.logger.info(f"mock destroy: {id} for {email}")
55
55
56
56
57
57
class ShellScriptVirtualization(VirtualizationInterface):
···
74
74
completedProcess = run(my_args, capture_output=True)
75
75
76
76
if completedProcess.returncode != 0:
77
-
print(f"""
77
+
current_app.logger.error(f"""
78
78
capacity-avaliable.sh exited {completedProcess.returncode} with
79
79
stdout:
80
80
{completedProcess.stdout}
···
85
85
86
86
lines = completedProcess.stdout.splitlines()
87
87
if not lines[len(lines)-1] == "yes":
88
-
print("capacity-avaliable.sh exited 0 but did not return \"yes\" ")
88
+
current_app.logger.error("capacity-avaliable.sh exited 0 but did not return \"yes\" ")
89
89
return False
90
90
91
91
return True