aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCara Salter <cara@devcara.com>2022-09-21 21:33:40 -0400
committerCara Salter <cara@devcara.com>2022-09-21 21:33:40 -0400
commitf9b99ce66f56995a29709e9bf24750dab9430767 (patch)
tree3624255932ab500bcfa3043932acd31b23fe86b3
parentb1ffd5220866dc9479fa284dfb2f0a0e111a6031 (diff)
downloadnccd-f9b99ce66f56995a29709e9bf24750dab9430767.tar.gz
nccd-f9b99ce66f56995a29709e9bf24750dab9430767.zip
bunch of features
registration, logging out, listing networks, user profiles
-rw-r--r--.flaskenv2
-rw-r--r--app/__init__.py2
-rw-r--r--app/auth/__init__.py41
-rw-r--r--app/auth/forms.py1
-rw-r--r--app/config.py1
-rw-r--r--app/database.py27
-rw-r--r--app/manage/__init__.py50
-rw-r--r--app/manage/forms.py8
-rw-r--r--app/meta/__init__.py1
-rw-r--r--app/static/gen/style.css46
-rw-r--r--app/static/scss/style.scss10
-rw-r--r--app/templates/base.html11
-rw-r--r--app/templates/index.html8
-rw-r--r--app/templates/network_list.html42
-rw-r--r--app/templates/profile.html46
-rw-r--r--app/templates/register.html17
-rw-r--r--migrations/versions/44e4ecea108b_.py34
-rw-r--r--migrations/versions/49b3fa6a4173_add_manager_to_network.py30
-rw-r--r--migrations/versions/4acd1ace5eb6_.py44
-rw-r--r--migrations/versions/81173c1d4f0a_initial_migration.py45
-rw-r--r--migrations/versions/900a50c566c4_add_description_to_peers.py28
-rw-r--r--migrations/versions/afd561b2a827_add_admin_field.py28
-rw-r--r--migrations/versions/bc8f89f077d8_uniquify_network_subnet.py28
-rw-r--r--migrations/versions/e7963e59fb3c_.py30
24 files changed, 496 insertions, 84 deletions
diff --git a/.flaskenv b/.flaskenv
index 37195f2..f8ed3ab 100644
--- a/.flaskenv
+++ b/.flaskenv
@@ -1,2 +1,2 @@
-FLASK_APP=app.py:create_app
+FLASK_APP=app:create_app
FLASK_ENV=development
diff --git a/app/__init__.py b/app/__init__.py
index a2486ed..37c992e 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -30,11 +30,13 @@ def create_app():
from .database import User, Role
from . import auth
from . import meta
+ from . import manage
# Blueprints
app.register_blueprint(auth.bp)
app.register_blueprint(meta.bp)
+ app.register_blueprint(manage.bp)
print(app.url_map)
diff --git a/app/auth/__init__.py b/app/auth/__init__.py
index b419351..06faa6d 100644
--- a/app/auth/__init__.py
+++ b/app/auth/__init__.py
@@ -1,6 +1,8 @@
-from flask import Blueprint, request, redirect, url_for, flash, render_template
-from flask_login import current_user, login_user
+from flask import Blueprint, request, redirect, url_for, flash, render_template, current_app
+from flask_login import current_user, login_user, login_required
+import flask_login
from werkzeug.security import check_password_hash, generate_password_hash
+from datetime import datetime
from app.auth.forms import LoginForm, RegisterForm
from app.database import User
@@ -22,15 +24,21 @@ def login():
email = request.form.get('username')
password = request.form.get('password')
- u = User.query.fetch_one().filter_by(email=email)
+ u = User.query.filter_by(email=email).first()
if u is not None:
if check_password_hash(u.password, password):
- login_user(u)
+ if u.active:
+ login_user(u)
- flash("Logged in successfully")
+ u.last_login = datetime.now()
+ db.session.commit()
+
+ flash("Logged in successfully")
- return redirect(url_for("meta.home"))
+ return redirect(url_for("meta.home"))
+ else:
+ flash("User is inactive. Contact an administrator")
else:
flash("Incorrect password")
@@ -57,7 +65,7 @@ def register():
# Passwords match
user = User(
- id=str(ulid.new()),
+ id=str(ulid.ulid()),
email=email,
password=generate_password_hash(password),
pref_name=pref_name,
@@ -73,4 +81,21 @@ def register():
else:
flash("Passwords do not match")
- return render_template("register.html")
+ return render_template("register.html", form=form)
+
+
+@bp.route("/logout")
+@login_required
+def logout():
+ flask_login.logout_user()
+
+ return redirect("/")
+
+@bp.route("/profile")
+@login_required
+def profile():
+ debug = current_app.config['DEBUG']
+ peers = current_user.peers
+ networks = current_user.networks
+ return render_template("profile.html", debug=debug, peers=peers,
+ nets=networks)
diff --git a/app/auth/forms.py b/app/auth/forms.py
index 778e4fb..ce8ed4c 100644
--- a/app/auth/forms.py
+++ b/app/auth/forms.py
@@ -9,6 +9,7 @@ class LoginForm(FlaskForm):
class RegisterForm(FlaskForm):
username = StringField("Email", validators=[DataRequired()])
+ pref_name = StringField("Preferred Name", validators=[DataRequired()])
password = PasswordField("Password", validators=[DataRequired()])
password_confirm = PasswordField("Confirm Password", validators=[DataRequired()])
submit = SubmitField("Register")
diff --git a/app/config.py b/app/config.py
index 2a157fa..fc37f36 100644
--- a/app/config.py
+++ b/app/config.py
@@ -6,6 +6,7 @@ def load_config(path):
result = {
'SQLALCHEMY_DATABASE_URI': contents['database']['postgres_url'],
'SECRET_KEY': contents['server']['secret_key'],
+ 'DEBUG': contents['server']['debug'],
'SECURITY_REGISTERABLE': True,
'SECURITY_PASSWORD_SALT': contents['server']['secret_key']
}
diff --git a/app/database.py b/app/database.py
index 532f971..c66f603 100644
--- a/app/database.py
+++ b/app/database.py
@@ -5,15 +5,24 @@ from . import db
from . import login
from flask_login import UserMixin
+from flask import redirect, url_for, flash
+
class User(db.Model, UserMixin):
id = Column(String, primary_key=True)
email = Column(String, unique=True, nullable=False)
password = Column(String, nullable=False)
pref_name = Column(String, nullable=False)
last_login = Column(DateTime, nullable=False)
- fs_uniquifier = Column(String, unique=True, nullable=False)
active = Column(Boolean, nullable=False)
+ is_admin = Column(Boolean, nullable=False, default=False)
roles = relationship('Role', secondary='roles_users',backref=backref('users', lazy='dynamic'))
+ peers = relationship('Peer', backref='owner', lazy=True)
+ networks = relationship('Network', backref="manager", lazy=True)
+
+ def __repr__(self):
+ return f"<User {email}>"
+ def __str__(self):
+ return self.pref_name
class Role(db.Model):
id = Column(String, primary_key=True)
@@ -28,15 +37,27 @@ class RolesUsers(db.Model):
@login.user_loader
def load_user(user_id):
- return User.query.filer_by(id=user_id)
+ return User.query.filter_by(id=user_id).first()
+@login.unauthorized_handler
+def unauth():
+ flash("Please log in first")
+ return redirect(url_for("auth.login"))
class Peer(db.Model):
id = Column(String, primary_key=True)
addr = Column(CIDR, nullable=False)
public_key = Column(String, nullable=False)
+ description = Column(String, nullable=False)
+ owner_id = Column(String, ForeignKey('user.id'), nullable=False)
class Network(db.Model):
id = Column(String, primary_key=True)
- subnet = Column(CIDR, nullable=False)
+ subnet = Column(CIDR, nullable=False, unique=True)
description = Column(String, nullable=True)
+ manager_id = Column(String, ForeignKey('user.id'), nullable=False)
+
+ def __repr__(self):
+ return f"{self.description} ({self.subnet})"
+ def __str__(self):
+ return f"{self.description} ({self.subnet})"
diff --git a/app/manage/__init__.py b/app/manage/__init__.py
new file mode 100644
index 0000000..c69376f
--- /dev/null
+++ b/app/manage/__init__.py
@@ -0,0 +1,50 @@
+from flask import Blueprint, render_template, request, flash, redirect, url_for
+from flask_login import login_required, current_user
+import ulid
+
+from app import db
+from app.database import Network
+
+from .forms import NewNetworkForm
+
+bp = Blueprint('manage', __name__, url_prefix="/manage")
+
+@bp.route("/networks", methods=["GET", "POST"])
+@login_required
+def list_networks():
+ nets = current_user.networks
+
+ form = NewNetworkForm(request.form)
+
+ if request.method == "POST" and form.validate_on_submit():
+ subnet = request.form.get('subnet')
+ description = request.form.get('description')
+
+ n = Network(
+ id=str(ulid.ulid()),
+ subnet=subnet,
+ description=description,
+ manager_id=str(current_user.id)
+ )
+ db.session.add(n)
+ db.session.commit()
+
+ flash("Network added")
+
+ return render_template("network_list.html", nets=nets, form=form)
+
+@bp.route("/networks/<string:id>/delete")
+@login_required
+def del_net(id):
+ n = Network.query.filter_by(id=id).first()
+
+ if n.manager_id != current_user.id:
+ flash("You aren't a manager of this network.")
+ return redirect(url_for("manage.list_networks"))
+
+ db.session.delete(n)
+ db.session.commit()
+
+ flash("Network deleted")
+
+ return redirect(url_for("manage.list_networks"))
diff --git a/app/manage/forms.py b/app/manage/forms.py
new file mode 100644
index 0000000..0849367
--- /dev/null
+++ b/app/manage/forms.py
@@ -0,0 +1,8 @@
+from flask_wtf import FlaskForm
+from wtforms.fields.simple import PasswordField, StringField, SubmitField
+from wtforms.validators import DataRequired, Regexp
+
+class NewNetworkForm(FlaskForm):
+ subnet = StringField("Subnet CIDR", validators=[DataRequired(), Regexp(r"^([0-9]{1,3}\.){3}[0-9]{1,3}($|/(8|16|24|32))$")])
+ description = StringField("Description", validators=[DataRequired()])
+ submit = SubmitField("Create")
diff --git a/app/meta/__init__.py b/app/meta/__init__.py
index 4fbe23c..8b6f6fa 100644
--- a/app/meta/__init__.py
+++ b/app/meta/__init__.py
@@ -6,5 +6,4 @@ bp = Blueprint("meta", __name__)
@bp.route("/")
def home():
- flash("Test")
return render_template("index.html")
diff --git a/app/static/gen/style.css b/app/static/gen/style.css
index 9bca965..5159ee1 100644
--- a/app/static/gen/style.css
+++ b/app/static/gen/style.css
@@ -1,17 +1,14 @@
body {
background: #282828;
color: #ebdbb2;
- font-family: monospace;
-}
+ font-family: monospace; }
a a:active, a:visited {
- color: #458588;
-}
+ color: #458588; }
.container {
margin: auto;
- width: 60%;
-}
+ width: 60%; }
button,
input[type=submit] {
@@ -19,13 +16,15 @@ input[type=submit] {
background-color: #458588;
border-color: #458588;
border: none;
- margin: 0.5rem;
-}
+ margin: 0.5rem; }
button.accent {
background-color: #d79921;
- border-color: #d79921;
-}
+ border-color: #d79921; }
+
+h1, h2, h3, h4, h5, h6 {
+ border-bottom: 1px solid;
+ width: 50%; }
.navbar {
list-style-type: none;
@@ -34,44 +33,39 @@ button.accent {
border-bottom: 1px solid;
margin-bottom: 2rem;
padding-bottom: 0.4rem;
- text-align: center;
-}
+ text-align: center; }
.navbar-item {
display: inline;
- margin-right: 1rem;
-}
+ margin-right: 1rem; }
.flashes {
list-style-type: none;
display: flex;
-}
+ justify-content: center; }
.message {
- width: 30%;
+ width: 80%;
justify-content: center;
border: 1px solid #ebdbb2;
background-color: #d79921;
- color: black;
-}
+ padding: 0.2rem;
+ font-size: large;
+ color: black; }
form {
- width: 40%;
-}
+ width: 40%; }
label,
input {
margin-bottom: 0.5rem;
margin-top: 0.5rem;
- display: inline-block;
-}
+ display: inline-block; }
label {
width: 40%;
- text-align: left;
-}
+ text-align: left; }
label + input {
width: 40%;
- margin: 0 30% 0 4%;
-}
+ margin: 0 30% 0 4%; }
diff --git a/app/static/scss/style.scss b/app/static/scss/style.scss
index cc23959..7c8760c 100644
--- a/app/static/scss/style.scss
+++ b/app/static/scss/style.scss
@@ -36,6 +36,11 @@ button.accent {
border-color: $color-accent-bg;
}
+h1,h2,h3,h4,h5,h6 {
+ border-bottom: 1px solid;
+ width:50%;
+}
+
// Navbar
.navbar {
@@ -56,13 +61,16 @@ button.accent {
.flashes {
list-style-type: none;
display: flex;
+ justify-content: center;
}
.message {
- width: 30%;
+ width: 80%;
justify-content: center;
border: 1px solid $color-fg;
background-color: $color-accent-bg;
+ padding: 0.2rem;
+ font-size: large;
color: black;
}
diff --git a/app/templates/base.html b/app/templates/base.html
index d905156..735d430 100644
--- a/app/templates/base.html
+++ b/app/templates/base.html
@@ -14,6 +14,17 @@
<div class="navbar">
<ul>
<li class="navbar-item"><a href="/">Home</a></li>
+ {% if not current_user.is_authenticated %}
+ <li class="navbar-item"><a
+ href="{{url_for('auth.login')}}">Login</a></li>
+ <li class="navbar-item"><a
+ href="{{url_for('auth.register')}}">Register</a></li>
+ {% else %}
+ <li class="navbar-item"><a
+ href="{{url_for('auth.profile')}}">Profile</a></li>
+ <li class="navbar-item"><a
+ href="{{url_for('auth.logout')}}">Logout</a></li>
+ {% endif %}
</ul>
</div>
{% with messages = get_flashed_messages() %}
diff --git a/app/templates/index.html b/app/templates/index.html
index 3c69e80..efaf29e 100644
--- a/app/templates/index.html
+++ b/app/templates/index.html
@@ -1,5 +1,13 @@
{% extends "base.html" %}
{% block content %}
+{% if current_user.is_authenticated %}
+<h1>Hi, {{current_user.pref_name}}!</h1>
+{% else %}
+<h1>Hello!</h1>
+<p>Much of this tool requires authentication. Head on over <a
+ href="{{url_for('auth.login')}}">here</a>
+to login, or <a href="{{url_for('auth.register')}}">here</a> to register.
+{% endif %}
{% endblock %}
diff --git a/app/templates/network_list.html b/app/templates/network_list.html
new file mode 100644
index 0000000..f56c6dc
--- /dev/null
+++ b/app/templates/network_list.html
@@ -0,0 +1,42 @@
+{% extends 'base.html' %}
+
+{% block content %}
+<h1>Managed Network Subnets</h1>
+
+<table>
+ <thead>
+ <tr>
+ <th scope="col">ID</th>
+ <th scope="col">Subnet</th>
+ <th scope="col">Description</th>
+ <th scope="col">Manager</th>
+ <th scope="col">Delete</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for n in nets %}
+ <tr>
+ <td>{{n.id}}</td>
+ <td>{{n.subnet}}</td>
+ <td>{{n.description}}</td>
+ <td>{{n.manager_id}}</td>
+ <td><a href="{{url_for('manage.del_net', id=n.id)}}">Delete</a></td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+
+<p>Want to add another one?</p>
+<form method="POST">
+ {{form.csrf_token}}
+ <div>
+ {{form.description.label}} {{form.description}}
+ </div>
+ <div>
+ {{form.subnet.label}} {{form.subnet}}
+ </div>
+ <div>
+ {{form.submit}}
+ </div>
+ </form>
+{% endblock %}
diff --git a/app/templates/profile.html b/app/templates/profile.html
new file mode 100644
index 0000000..6d4d60a
--- /dev/null
+++ b/app/templates/profile.html
@@ -0,0 +1,46 @@
+{% extends 'base.html' %}
+
+{% block content %}
+<h1>{{current_user.pref_name}} - {{current_user.email}} ({{current_user.id}})</h1>
+
+{% if debug %}
+<table>
+ <thead>
+ <tr>
+ <th scope="col">Key</th>
+ <th scope="col">Value</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for attr, value in current_user.__dict__.items() %}
+ <tr>
+ <td>{{attr}}</td>
+ <td>{{value}}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+{% endif %}
+
+<h3>Owned Peers</h3>
+<table>
+ <thead>
+ <tr>
+ <th scope="col">ID</th>
+ <th scope="col">Description</th>
+ <th scope="col">Address</th>
+ <th scope="col">Public Key</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for p in peers %}
+ <tr>
+ <td>{{p.id}}</td>
+ <td>{{p.description}}</td>
+ <td>{{p.addr}}</td>
+ <td>{{p.public_key}}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+{% endblock %}
diff --git a/app/templates/register.html b/app/templates/register.html
index 56d9855..6e19559 100644
--- a/app/templates/register.html
+++ b/app/templates/register.html
@@ -1,5 +1,22 @@
{% extends 'base.html' %}
{% block content %}
+<form method="POST">
+ {{ form.csrf_token }}
+ <div>
+ {{form.username.label}} {{form.username}}
+ </div>
+ <div>
+ {{form.pref_name.label}} {{form.pref_name}}
+ </div>
+ <div>
+ <span>
+ {{form.password.label}} {{form.password}}
+ {{form.password_confirm.label}} {{form.password_confirm}}
+ </span>
+ </div>
+
+ {{form.submit}}
+</form>
{% endblock %}
diff --git a/migrations/versions/44e4ecea108b_.py b/migrations/versions/44e4ecea108b_.py
new file mode 100644
index 0000000..c225770
--- /dev/null
+++ b/migrations/versions/44e4ecea108b_.py
@@ -0,0 +1,34 @@
+"""empty message
+
+Revision ID: 44e4ecea108b
+Revises: e7963e59fb3c
+Create Date: 2022-09-21 12:33:11.723559
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '44e4ecea108b'
+down_revision = 'e7963e59fb3c'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('peer', sa.Column('owner_id', sa.String(), nullable=False))
+ op.drop_constraint('peer_owner_fkey', 'peer', type_='foreignkey')
+ op.create_foreign_key(None, 'peer', 'user', ['owner_id'], ['id'])
+ op.drop_column('peer', 'owner')
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('peer', sa.Column('owner', sa.VARCHAR(), autoincrement=False, nullable=False))
+ op.drop_constraint(None, 'peer', type_='foreignkey')
+ op.create_foreign_key('peer_owner_fkey', 'peer', 'user', ['owner'], ['id'])
+ op.drop_column('peer', 'owner_id')
+ # ### end Alembic commands ###
diff --git a/migrations/versions/49b3fa6a4173_add_manager_to_network.py b/migrations/versions/49b3fa6a4173_add_manager_to_network.py
new file mode 100644
index 0000000..028c286
--- /dev/null
+++ b/migrations/versions/49b3fa6a4173_add_manager_to_network.py
@@ -0,0 +1,30 @@
+"""add manager to network
+
+Revision ID: 49b3fa6a4173
+Revises: 44e4ecea108b
+Create Date: 2022-09-21 12:37:26.465495
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '49b3fa6a4173'
+down_revision = '44e4ecea108b'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('network', sa.Column('manager_id', sa.String(), nullable=False))
+ op.create_foreign_key(None, 'network', 'user', ['manager_id'], ['id'])
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_constraint(None, 'network', type_='foreignkey')
+ op.drop_column('network', 'manager_id')
+ # ### end Alembic commands ###
diff --git a/migrations/versions/4acd1ace5eb6_.py b/migrations/versions/4acd1ace5eb6_.py
new file mode 100644
index 0000000..da39eb2
--- /dev/null
+++ b/migrations/versions/4acd1ace5eb6_.py
@@ -0,0 +1,44 @@
+"""empty message
+
+Revision ID: 4acd1ace5eb6
+Revises: 946e687d2d42
+Create Date: 2022-09-21 10:20:39.896514
+
+"""
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = '4acd1ace5eb6'
+down_revision = '946e687d2d42'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('network',
+ sa.Column('id', sa.String(), nullable=False),
+ sa.Column('subnet', postgresql.CIDR(), nullable=False),
+ sa.Column('description', sa.String(), nullable=True),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_table('peer',
+ sa.Column('id', sa.String(), nullable=False),
+ sa.Column('addr', postgresql.CIDR(), nullable=False),
+ sa.Column('public_key', sa.String(), nullable=False),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.drop_constraint('user_fs_uniquifier_key', 'user', type_='unique')
+ op.drop_column('user', 'fs_uniquifier')
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('user', sa.Column('fs_uniquifier', sa.VARCHAR(), autoincrement=False, nullable=False))
+ op.create_unique_constraint('user_fs_uniquifier_key', 'user', ['fs_uniquifier'])
+ op.drop_table('peer')
+ op.drop_table('network')
+ # ### end Alembic commands ###
diff --git a/migrations/versions/81173c1d4f0a_initial_migration.py b/migrations/versions/81173c1d4f0a_initial_migration.py
index f96ff21..4345556 100644
--- a/migrations/versions/81173c1d4f0a_initial_migration.py
+++ b/migrations/versions/81173c1d4f0a_initial_migration.py
@@ -37,54 +37,11 @@ def upgrade():
sa.UniqueConstraint('email'),
sa.UniqueConstraint('fs_uniquifier')
)
- op.drop_table('enroll_requests')
- op.drop_table('_sqlx_migrations')
- op.drop_table('networks')
- op.drop_table('peers')
- op.drop_table('users')
- # ### end Alembic commands ###
+ # ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.create_table('users',
- sa.Column('id', sa.TEXT(), autoincrement=False, nullable=False),
- sa.Column('email', sa.TEXT(), autoincrement=False, nullable=False),
- sa.Column('pref_name', sa.TEXT(), autoincrement=False, nullable=False),
- sa.Column('pw_hash', sa.TEXT(), autoincrement=False, nullable=False),
- sa.Column('last_login', postgresql.TIMESTAMP(), autoincrement=False, nullable=True),
- sa.PrimaryKeyConstraint('id', name='users_pkey'),
- postgresql_ignore_search_path=False
- )
- op.create_table('peers',
- sa.Column('id', sa.TEXT(), autoincrement=False, nullable=False),
- sa.Column('addr', postgresql.CIDR(), autoincrement=False, nullable=False),
- sa.Column('public_key', sa.TEXT(), autoincrement=False, nullable=False),
- sa.Column('owner', sa.TEXT(), autoincrement=False, nullable=False),
- sa.ForeignKeyConstraint(['owner'], ['users.id'], name='peers_owner_fkey'),
- sa.PrimaryKeyConstraint('id', name='peers_pkey')
- )
- op.create_table('networks',
- sa.Column('id', sa.TEXT(), autoincrement=False, nullable=False),
- sa.Column('subnet', postgresql.CIDR(), autoincrement=False, nullable=False),
- sa.Column('description', sa.TEXT(), autoincrement=False, nullable=True),
- sa.PrimaryKeyConstraint('id', name='networks_pkey')
- )
- op.create_table('_sqlx_migrations',
- sa.Column('version', sa.BIGINT(), autoincrement=False, nullable=False),
- sa.Column('description', sa.TEXT(), autoincrement=False, nullable=False),
- sa.Column('installed_on', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=False),
- sa.Column('success', sa.BOOLEAN(), autoincrement=False, nullable=False),
- sa.Column('checksum', postgresql.BYTEA(), autoincrement=False, nullable=False),
- sa.Column('execution_time', sa.BIGINT(), autoincrement=False, nullable=False),
- sa.PrimaryKeyConstraint('version', name='_sqlx_migrations_pkey')
- )
- op.create_table('enroll_requests',
- sa.Column('token', sa.TEXT(), autoincrement=False, nullable=False),
- sa.Column('expires', postgresql.TIMESTAMP(), server_default=sa.text("(CURRENT_TIMESTAMP + ((30)::double precision * '00:01:00'::interval))"), autoincrement=False, nullable=False),
- sa.Column('confirmed', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False),
- sa.PrimaryKeyConstraint('token', name='enroll_requests_pkey')
- )
op.drop_table('user')
op.drop_table('role')
# ### end Alembic commands ###
diff --git a/migrations/versions/900a50c566c4_add_description_to_peers.py b/migrations/versions/900a50c566c4_add_description_to_peers.py
new file mode 100644
index 0000000..de751bf
--- /dev/null
+++ b/migrations/versions/900a50c566c4_add_description_to_peers.py
@@ -0,0 +1,28 @@
+"""add description to peers
+
+Revision ID: 900a50c566c4
+Revises: 49b3fa6a4173
+Create Date: 2022-09-21 12:43:37.943280
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '900a50c566c4'
+down_revision = '49b3fa6a4173'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('peer', sa.Column('description', sa.String(), nullable=False))
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('peer', 'description')
+ # ### end Alembic commands ###
diff --git a/migrations/versions/afd561b2a827_add_admin_field.py b/migrations/versions/afd561b2a827_add_admin_field.py
new file mode 100644
index 0000000..da3552a
--- /dev/null
+++ b/migrations/versions/afd561b2a827_add_admin_field.py
@@ -0,0 +1,28 @@
+"""add admin field
+
+Revision ID: afd561b2a827
+Revises: bc8f89f077d8
+Create Date: 2022-09-21 16:10:37.422259
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'afd561b2a827'
+down_revision = 'bc8f89f077d8'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('user', sa.Column('is_admin', sa.Boolean(), nullable=False, server_default=str(False)))
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('user', 'is_admin')
+ # ### end Alembic commands ###
diff --git a/migrations/versions/bc8f89f077d8_uniquify_network_subnet.py b/migrations/versions/bc8f89f077d8_uniquify_network_subnet.py
new file mode 100644
index 0000000..e216a79
--- /dev/null
+++ b/migrations/versions/bc8f89f077d8_uniquify_network_subnet.py
@@ -0,0 +1,28 @@
+"""uniquify network subnet
+
+Revision ID: bc8f89f077d8
+Revises: 900a50c566c4
+Create Date: 2022-09-21 16:01:43.914719
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'bc8f89f077d8'
+down_revision = '900a50c566c4'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_unique_constraint(None, 'network', ['subnet'])
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_constraint(None, 'network', type_='unique')
+ # ### end Alembic commands ###
diff --git a/migrations/versions/e7963e59fb3c_.py b/migrations/versions/e7963e59fb3c_.py
new file mode 100644
index 0000000..dbed101
--- /dev/null
+++ b/migrations/versions/e7963e59fb3c_.py
@@ -0,0 +1,30 @@
+"""empty message
+
+Revision ID: e7963e59fb3c
+Revises: 4acd1ace5eb6
+Create Date: 2022-09-21 12:31:24.175307
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'e7963e59fb3c'
+down_revision = '4acd1ace5eb6'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('peer', sa.Column('owner', sa.String(), nullable=False))
+ op.create_foreign_key(None, 'peer', 'user', ['owner'], ['id'])
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_constraint(None, 'peer', type_='foreignkey')
+ op.drop_column('peer', 'owner')
+ # ### end Alembic commands ###