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 %}
+
+ {% 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 %}
-
- {% 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
-
+