add sql cli and explain it in the readme
This commit is contained in:
parent
8de802aff5
commit
0dc58ed6a8
49
README.md
49
README.md
@ -42,7 +42,7 @@ nano capsulflask/__init__.py
|
||||
Run the app
|
||||
|
||||
```
|
||||
FLASK_APP=capsulflask flask run
|
||||
flask run
|
||||
```
|
||||
|
||||
Run the app in gunicorn
|
||||
@ -50,8 +50,49 @@ Run the app in gunicorn
|
||||
.venv/bin/gunicorn --bind 127.0.0.1:5000 capsulflask:app
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
# postgres database schema management
|
||||
## cli
|
||||
|
||||
You can manually mess around with the database like this:
|
||||
|
||||
```
|
||||
flask cli sql -f test.sql
|
||||
```
|
||||
|
||||
```
|
||||
flask cli sql -c 'SELECT * FROM vms'
|
||||
```
|
||||
|
||||
This one selects the vms table with the column name header:
|
||||
|
||||
```
|
||||
flask cli sql -c "SELECT string_agg(column_name::text, ', ') from information_schema.columns WHERE table_name='vms'; SELECT * from vms"
|
||||
```
|
||||
|
||||
How to modify a payment manually, like if you get a chargeback or to fix customer payment issues:
|
||||
|
||||
```
|
||||
$ flask cli sql -c "SELECT id, created, email, dollars, invalidated from payments"
|
||||
1, 2020-05-05T00:00:00, forest.n.johnson@gmail.com, 20.00, FALSE
|
||||
|
||||
$ flask cli sql -c "UPDATE payments SET invalidated = True WHERE id = 1"
|
||||
1 rows affected.
|
||||
|
||||
$ flask cli sql -c "SELECT id, created, email, dollars, invalidated from payments"
|
||||
1, 2020-05-05T00:00:00, forest.n.johnson@gmail.com, 20.00, TRUE
|
||||
```
|
||||
|
||||
|
||||
How you would kick off the scheduled task:
|
||||
|
||||
```
|
||||
flask cli cron-task
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## postgres database schema management
|
||||
|
||||
capsulflask has a concept of a schema version. When the application starts, it will query the database for a table named
|
||||
`schemaversion` that has one row and one column (`version`). If the `version` it finds is not equal to the `desiredSchemaVersion` variable set in `db.py`, it will run migration scripts from the `schema_migrations` folder one by one until the `schemaversion` table shows the correct version.
|
||||
@ -62,9 +103,9 @@ For example, the script named `02_up_xyz.sql` should contain code that migrates
|
||||
|
||||
In general, for safety, schema version upgrades should not delete data. Schema version downgrades will simply throw an error and exit for now.
|
||||
|
||||
-----
|
||||
|
||||
|
||||
# how to setup btcpay server
|
||||
## how to setup btcpay server
|
||||
|
||||
Generate a private key and the accompanying bitpay SIN for the bitpay API client.
|
||||
|
||||
|
@ -8,7 +8,7 @@ from flask import Flask
|
||||
from flask_mail import Mail
|
||||
from flask import render_template
|
||||
|
||||
from capsulflask import virt_model
|
||||
from capsulflask import virt_model, cli
|
||||
|
||||
load_dotenv(find_dotenv())
|
||||
|
||||
@ -48,13 +48,14 @@ from capsulflask import db
|
||||
|
||||
db.init_app(app)
|
||||
|
||||
from capsulflask import auth, landing, console, payment, metrics
|
||||
from capsulflask import auth, landing, console, payment, metrics, cli
|
||||
|
||||
app.register_blueprint(landing.bp)
|
||||
app.register_blueprint(auth.bp)
|
||||
app.register_blueprint(console.bp)
|
||||
app.register_blueprint(payment.bp)
|
||||
app.register_blueprint(metrics.bp)
|
||||
app.register_blueprint(cli.bp)
|
||||
|
||||
app.add_url_rule("/", endpoint="index")
|
||||
|
||||
|
63
capsulflask/cli.py
Normal file
63
capsulflask/cli.py
Normal file
@ -0,0 +1,63 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
import click
|
||||
from flask.cli import with_appcontext
|
||||
from flask import Blueprint
|
||||
from psycopg2 import ProgrammingError
|
||||
|
||||
from capsulflask.db import get_model, my_exec_info_message
|
||||
|
||||
bp = Blueprint('cli', __name__)
|
||||
|
||||
@bp.cli.command('sql')
|
||||
@click.option('-f', help='script filename')
|
||||
@click.option('-c', help='sql command')
|
||||
@with_appcontext
|
||||
def sql_script(f, c):
|
||||
"""Run a sql script against the database. script is run 1 command at a time inside a single transaction."""
|
||||
|
||||
model = get_model()
|
||||
script = ""
|
||||
if f:
|
||||
filepath = os.path.join(os.getcwd(), f)
|
||||
if not os.path.isfile(filepath):
|
||||
raise f"{filepath} is not a file"
|
||||
|
||||
with open(filepath, 'rb') as file:
|
||||
script = file.read().decode("utf8")
|
||||
elif c:
|
||||
script = c
|
||||
else:
|
||||
click.echo(f"you must provide sql to run either inline with the -c argument or in a file with the -f argument")
|
||||
return
|
||||
|
||||
commands = re.split(";\\s+", script)
|
||||
|
||||
for command in commands:
|
||||
if command.strip() != "":
|
||||
|
||||
model.cursor.execute(command)
|
||||
if re.match("^\\s*select", command, re.IGNORECASE) is not None:
|
||||
for row in model.cursor.fetchall():
|
||||
def format_value(x):
|
||||
if isinstance(x, bool):
|
||||
return "TRUE" if x else "FALSE"
|
||||
if not x :
|
||||
return "null"
|
||||
if isinstance(x, datetime):
|
||||
return x.isoformat()
|
||||
return f"{x}"
|
||||
|
||||
click.echo(", ".join(list(map(format_value, row))))
|
||||
else:
|
||||
click.echo(f"{model.cursor.rowcount} rows affected.")
|
||||
|
||||
model.connection.commit()
|
||||
|
||||
@bp.cli.command('cron-task')
|
||||
@with_appcontext
|
||||
def cron_task():
|
||||
print('a')
|
@ -146,12 +146,12 @@ class DBModel:
|
||||
|
||||
def list_payments_for_account(self, email):
|
||||
self.cursor.execute("""
|
||||
SELECT dollars, invalidated, created
|
||||
SELECT id, dollars, invalidated, created
|
||||
FROM payments WHERE payments.email = %s""",
|
||||
(email, )
|
||||
)
|
||||
return list(map(
|
||||
lambda x: dict(dollars=x[0], invalidated=x[1], created=x[2]),
|
||||
lambda x: dict(id=x[0], dollars=x[1], invalidated=x[2], created=x[3]),
|
||||
self.cursor.fetchall()
|
||||
))
|
||||
|
||||
|
@ -49,11 +49,12 @@ CREATE TABLE vm_ssh_public_key (
|
||||
);
|
||||
|
||||
CREATE TABLE payments (
|
||||
id SERIAL,
|
||||
email TEXT REFERENCES accounts(email) ON DELETE RESTRICT,
|
||||
created TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
dollars NUMERIC(8, 2) NOT NULL,
|
||||
invalidated BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY (email, created)
|
||||
PRIMARY KEY (email, id)
|
||||
);
|
||||
|
||||
CREATE TABLE login_tokens (
|
||||
@ -74,8 +75,8 @@ CREATE TABLE payment_sessions (
|
||||
CREATE TABLE unconfirmed_btcpay_invoices (
|
||||
id TEXT PRIMARY KEY,
|
||||
email TEXT REFERENCES accounts(email) ON DELETE RESTRICT,
|
||||
created TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (email, created) REFERENCES payments(email, created) ON DELETE CASCADE
|
||||
payment_id INTEGER NOT NULL,
|
||||
FOREIGN KEY (email, payment_id) REFERENCES payments(email, id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
INSERT INTO os_images (id, template_image_file_name, description)
|
||||
@ -100,7 +101,7 @@ 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-05-05','YYYY-MM-DD'));
|
||||
VALUES ('forest.n.johnson@gmail.com', 20.00, TO_TIMESTAMP('2020-05-05','YYYY-MM-DDTHH24-MI-SS'));
|
||||
|
||||
INSERT INTO vms (id, email, os, size)
|
||||
VALUES ('capsul-yi9ffqbjly', 'forest.n.johnson@gmail.com', 'alpine311', 'f1-xx');
|
||||
|
Loading…
Reference in New Issue
Block a user