diff --git a/capsulflask/console.py b/capsulflask/console.py index 161e386..7b219bc 100644 --- a/capsulflask/console.py +++ b/capsulflask/console.py @@ -1,3 +1,4 @@ +import re from flask import Blueprint from flask import flash from flask import current_app @@ -24,7 +25,62 @@ def makeCapsulId(): @bp.route("/") @account_required def index(): - return render_template("console.html", vms=get_model().list_vms_for_account(session["account"])) + return render_template("capsuls.html", vms=get_model().list_vms_for_account(session["account"])) + +@bp.route("/ssh", methods=("GET", "POST")) +@account_required +def ssh_public_keys(): + db_model = get_model() + error = None + + if request.method == "POST": + method = request.form["method"] + + name = request.form["name"] + if not name or len(name.strip()) < 1: + error = "Name is required" + elif not re.match(r"^[0-9A-Za-z_ -]+$", name): + error = "Name must match \"^[0-9A-Za-z_ -]+$\"" + + if method == "POST": + content = request.form["content"] + if not content or len(content.strip()) < 1: + error = "Content is required" + else: + content = content.replace("\r", "").replace("\n", "") + if not re.match(r"^(ssh|ecdsa)-[0-9A-Za-z+/_=@ -]+$", content): + error = "Content must match \"^(ssh|ecdsa)-[0-9A-Za-z+/_=@ -]+$\"" + + if db_model.ssh_public_key_name_exists(session["account"], name): + error = "A key with that name already exists" + + if error is None: + db_model.create_ssh_public_key(session["account"], name, content) + + elif method == "DELETE": + + if error is None: + db_model.delete_ssh_public_key(session["account"], name) + + if error: + flash(error) + + # keys_list= + # for key in keys_list: + # if len(key['content']) > 40: + # print(key['content']) + # print(f"{key['content'][:20]}...{key['content'][len(key['content'])-20:]}") + # key["content"] = + + # return + + return render_template( + "ssh-public-keys.html", + ssh_public_keys=map( + lambda x: dict(name=x['name'], content=f"{x['content'][:20]}...{x['content'][len(x['content'])-20:]}"), + db_model.list_ssh_public_keys_for_account(session["account"]) + ) + ) @bp.route("/create", methods=("GET", "POST")) @account_required @@ -64,8 +120,11 @@ def create(): memory=vm_sizes[size].memory ) + if error: + flash(error) + return render_template( - "create.html", + "create-capsul.html", ssh_public_keys=ssh_public_keys, operating_systems=operating_systems, vm_sizes=vm_sizes diff --git a/capsulflask/db_model.py b/capsulflask/db_model.py index 02c61fd..3e9f153 100644 --- a/capsulflask/db_model.py +++ b/capsulflask/db_model.py @@ -55,12 +55,29 @@ class DBModel: return vmSizes def list_ssh_public_keys_for_account(self, email): - self.cursor.execute("SELECT name, content FROM ssh_public_keys WHERE email = %s", (email, )) + self.cursor.execute("SELECT name, content, created FROM ssh_public_keys WHERE email = %s", (email, )) return map( - lambda x: dict(name=x[0], content=x[1]), + lambda x: dict(name=x[0], content=x[1], created=x[2]), self.cursor.fetchall() ) + def ssh_public_key_name_exists(self, email, name): + self.cursor.execute( "SELECT name FROM ssh_public_keys where email = %s AND name = %s", (email, name) ) + return len(self.cursor.fetchall()) > 0 + + def create_ssh_public_key(self, email, name, content): + self.cursor.execute(""" + INSERT INTO ssh_public_keys (email, name, content) + VALUES (%s, %s, %s) + """, + (email, name, content) + ) + self.connection.commit() + + def delete_ssh_public_key(self, email, name): + self.cursor.execute( "DELETE FROM ssh_public_keys where email = %s AND name = %s", (email, name) ) + self.connection.commit() + def list_vms_for_account(self, email): self.cursor.execute(""" SELECT vms.id, vms.last_seen_ipv4, vms.last_seen_ipv6, vms.size, os_images.description, vms.created, vms.deleted diff --git a/capsulflask/schema_migrations/02_up_accounts_vms_etc.sql b/capsulflask/schema_migrations/02_up_accounts_vms_etc.sql index 92664c4..745c24a 100644 --- a/capsulflask/schema_migrations/02_up_accounts_vms_etc.sql +++ b/capsulflask/schema_migrations/02_up_accounts_vms_etc.sql @@ -23,6 +23,7 @@ CREATE TABLE ssh_public_keys ( email TEXT REFERENCES accounts(email) ON DELETE RESTRICT, name TEXT NOT NULL, content TEXT NOT NULL, + created TIMESTAMP NOT NULL DEFAULT NOW(), PRIMARY KEY (email, name) ); @@ -42,8 +43,8 @@ CREATE TABLE vm_ssh_public_key ( ssh_public_key_name TEXT NOT NULL, email TEXT NOT NULL, vm_id TEXT NOT NULL, - FOREIGN KEY (email, ssh_public_key_name) REFERENCES ssh_public_keys(email, name) ON DELETE RESTRICT, - FOREIGN KEY (email, vm_id) REFERENCES vms(email, id) ON DELETE RESTRICT, + FOREIGN KEY (email, ssh_public_key_name) REFERENCES ssh_public_keys(email, name) ON DELETE CASCADE, + FOREIGN KEY (email, vm_id) REFERENCES vms(email, id) ON DELETE CASCADE, PRIMARY KEY (email, vm_id, ssh_public_key_name) ); @@ -78,4 +79,7 @@ VALUES ('f1-s', 5.33, 512, 1, 500), ('f1-xx', 29.66, 8192, 4, 8000), ('f1-xxx', 57.58, 16384, 8, 16000); +INSERT INTO accounts (email) +VALUES ('forest.n.johnson@gmail.com'); + UPDATE schemaversion SET version = 2; \ No newline at end of file diff --git a/capsulflask/static/style.css b/capsulflask/static/style.css index c7e4849..ad63369 100644 --- a/capsulflask/static/style.css +++ b/capsulflask/static/style.css @@ -93,11 +93,11 @@ label.align { min-width: 10em; } -input, select, label { +input, textarea, select, label { margin: 0.5em; } -input, select { +input, select, textarea { outline: 0; padding: 0.25em 0.5em; border-radius: 0.5em; @@ -118,7 +118,7 @@ select { background-size: 0.5em; } -input { +input, textarea { background: none; } @@ -126,18 +126,31 @@ input[type=text] { font: calc(0.40rem + 1vmin) monospace; border: 0; border-bottom: 1px solid #777e73; - min-width: 20em; outline: 0; } -input[type=submit], select { +input[type=text], textarea { + min-width: 20em; +} +input[type=text].expand, textarea.expand { + width: 100%; +} +textarea { + height: 6em; +} + +input[type=submit], select, textarea { font: calc(0.40rem + 1vmin) monospace; - cursor: pointer; border: 1px solid #777e73; background-color: #bdc7b810; } + +input[type=submit], select { + cursor: pointer; +} + h1, h2, h3, h4, h5 { font-size:calc(0.40rem + 1vmin); margin: initial; @@ -156,6 +169,13 @@ ul li { border: 1px solid #777e73; background: #bdc7b810; } +.break-word { + word-break: break-word; +} + +.dim { + color: #777e73bb; +} footer, p { text-align: left; @@ -172,3 +192,7 @@ footer { font-size: 1.8em; } +.smalltext { + font-size: 0.8em; +} + diff --git a/capsulflask/templates/base.html b/capsulflask/templates/base.html index 3d72c50..f825db6 100644 --- a/capsulflask/templates/base.html +++ b/capsulflask/templates/base.html @@ -27,7 +27,6 @@ {% if session["account"] %} Console - Billing {% endif %} Support diff --git a/capsulflask/templates/capsuls.html b/capsulflask/templates/capsuls.html new file mode 100644 index 0000000..0999f0c --- /dev/null +++ b/capsulflask/templates/capsuls.html @@ -0,0 +1,37 @@ +{% extends 'console-base.html' %} + +{% block title %}Console{% endblock %} +{% block consoletitle %}Console{% endblock %} + +{% block consolecontent %} + {% if vms[0] is defined %} + + + + + + + + + + + + {% for vm in vms %} + + + + + + + + + + {% endfor %} + +
idsizeipv4oscreated
{{ vm["id"] }}{{ vm["size"] }}{{ vm["ipv4"] }}{{ vm["os"] }}{{ vm["created"] }}
+ {% else %} + You don't have any Capsuls running. Create one today! + {% endif %} +{% endblock %} + +{% block pagesource %}/templates/capsuls.html{% endblock %} diff --git a/capsulflask/templates/console-base.html b/capsulflask/templates/console-base.html new file mode 100644 index 0000000..760768d --- /dev/null +++ b/capsulflask/templates/console-base.html @@ -0,0 +1,17 @@ +{% extends 'base.html' %} + +{% block content %} +
+

{% block consoletitle %}{% endblock %}

+
+
+ +
+
+ {% block consolecontent %}{% endblock %} +
+{% endblock %} diff --git a/capsulflask/templates/console.html b/capsulflask/templates/console.html deleted file mode 100644 index 3d8e1c4..0000000 --- a/capsulflask/templates/console.html +++ /dev/null @@ -1,49 +0,0 @@ -{% extends 'base.html' %} - -{% block title %}Console{% endblock %} - -{% block content %} -
-

CONSOLE

-
-
- -
-
- {% if vms[0] is defined %} - - - - - - - - - - - - {% for vm in vms %} - - - - - - - - - - {% endfor %} - -
idsizeipv4oscreated
{{ vm["id"] }}{{ vm["size"] }}{{ vm["ipv4"] }}{{ vm["os"] }}{{ vm["created"] }}
- {% else %} - You don't have any Capsuls running. Create one today! - {% endif %} - -
-{% endblock %} - -{% block pagesource %}/templates/console.html{% endblock %} diff --git a/capsulflask/templates/create.html b/capsulflask/templates/create-capsul.html similarity index 90% rename from capsulflask/templates/create.html rename to capsulflask/templates/create-capsul.html index ee3fd65..1831653 100644 --- a/capsulflask/templates/create.html +++ b/capsulflask/templates/create-capsul.html @@ -1,12 +1,10 @@ -{% extends 'base.html' %} +{% extends 'console-base.html' %} {% block title %}Create{% endblock %} +{% block consoletitle %}Console - Create Capsul{% endblock %} + +{% block consolecontent %} -{% block content %} -
-

CONSOLE - CREATE CAPSUL

-
-
{% if ssh_public_keys[0] is defined %}
 CAPSUL SIZES
@@ -22,7 +20,7 @@ f1-xxx   $57.58    8     16G     25G   16TB
 
 * net is calculated as a per-month average
 * all VMs come standard with one public IPv4 addr
-
+
@@ -65,4 +63,4 @@ f1-xxx $57.58 8 16G 25G 16TB {% endif %} {% endblock %} -{% block pagesource %}/templates/console.html{% endblock %} +{% block pagesource %}/templates/create-capsul.html{% endblock %} diff --git a/capsulflask/templates/faq.html b/capsulflask/templates/faq.html index 4d547b6..665475c 100644 --- a/capsulflask/templates/faq.html +++ b/capsulflask/templates/faq.html @@ -141,5 +141,5 @@ $ doas su - {% endblock %} -{% block pagesource %}/templates/changelog.html{% endblock %} +{% block pagesource %}/templates/faq.html{% endblock %} diff --git a/capsulflask/templates/ssh-public-keys.html b/capsulflask/templates/ssh-public-keys.html new file mode 100644 index 0000000..c9bf2a7 --- /dev/null +++ b/capsulflask/templates/ssh-public-keys.html @@ -0,0 +1,55 @@ +{% extends 'console-base.html' %} + +{% block title %}SSH Public Keys{% endblock %} +{% block consoletitle %}Console - SSH Public Keys{% endblock %} + +{% block consolecontent %} + + + {% if ssh_public_keys[0] is defined %}
{% endif %} + + {% for ssh_public_key in ssh_public_keys %} + + + +
+ {{ ssh_public_key['name'] }} + {{ ssh_public_key['content'] }} + +
+ + {% endfor %} + + {% if ssh_public_keys[0] is defined %}
{% endif %} + +
+

UPLOAD A NEW SSH KEY

+
+
+ +
+ + +
+
+ + +
+
+

Paste the contents of your SSH public key file here. + ( Something like ~/.ssh/id_rsa.pub ) +

+ The contents of this file should look similar to + ssh-rsa AAAAC3NzaC1l...Yqv== me@my-computer +

+ If you wish, you can + generate a new SSH key pair using the ssh-keygen utility. +

+
+
+ +
+
+{% endblock %} + +{% block pagesource %}/templates/ssh-public-keys.html{% endblock %}