diff --git a/app/admin/routes.py b/app/admin/routes.py index 91ba770..3bd71a9 100644 --- a/app/admin/routes.py +++ b/app/admin/routes.py @@ -38,9 +38,9 @@ def index(): @login_required @admin_required def list_items(): - alldeployments = Deployment.query.order_by(Deployment.date_last_charge.asc()).all() - alldomains = Domain.query.order_by(Domain.date_expire.asc()).all() - allservices = Service.query.order_by(Service.date_last_charge.asc()).all() + alldeployments = Deployment.query.order_by(Deployment.daysleft.asc()).all() + alldomains = Domain.query.order_by(Domain.daysleft.asc()).all() + allservices = Service.query.order_by(Service.daysleft.asc()).all() alladdresses = Address.query.order_by(Address.user_id.asc()).all() return render_template('admin/list_items.html', deployments=alldeployments, domains=alldomains, services=allservices, addresses=alladdresses) @@ -130,7 +130,7 @@ def dashboard(user_pid=0): send_email(current_app.config['MAIL_USERNAME'], 'Cube {} is unreachable'.format(cubeid), 'vmanager/email/adm_unreachable', user=cuser, cubeid=cubeid ) current_app.logger.info('[ADMIN] {} deployments: {}, services: {}, domains: {}, services: {}'.format(cuser.email, inv_deployments_list, inv_services_list, inv_domains_list, inv_addresses_list )) - return render_template('vmanager/dashboard.html', rrd=rrd, status=statuses, inv_deployments=inv_deployments, inv_services=inv_services, inv_domains=inv_domains, inv_addresses=inv_addresses, region=regions) + return render_template('main/dashboard.html', rrd=rrd, status=statuses, inv_deployments=inv_deployments, inv_services=inv_services, inv_domains=inv_domains, inv_addresses=inv_addresses, region=regions) @admin.route("/listtransactions", methods=['GET']) @login_required diff --git a/app/auth/routes.py b/app/auth/routes.py index e4dd5cf..e0c1858 100644 --- a/app/auth/routes.py +++ b/app/auth/routes.py @@ -71,7 +71,7 @@ def login(): db.session.commit() #send_email(current_app.config['MAIL_USERNAME'], user.email + ' logged in.', 'auth/email/adm_loginnotify', user=user, ipaddr=lastip ) flash('Last Login: {} from {}'.format(user.last_seen.strftime("%a %d %B %Y %H:%M"), previp)) - return redirect(request.args.get('next') or url_for('vmanager.dashboard')) + return redirect(request.args.get('next') or url_for('main.dashboard')) else: flash('Invalid username or password.') @@ -103,7 +103,7 @@ def twofactor(): db.session.add(user) db.session.commit() #send_email(current_app.config['MAIL_USERNAME'], user.email + ' logged in.', 'auth/email/adm_loginnotify', user=user, ipaddr=lastip ) - return redirect(request.args.get('next') or url_for('vmanager.dashboard')) + return redirect(request.args.get('next') or url_for('main.dashboard')) else: flash('Invalid token.') return render_template('auth/2fa.html', page=page, form=form) @@ -146,9 +146,9 @@ def register(): user = User(email=form.email.data, password=form.password.data, wallet=current_app.config['REGISTER_BONUS']) db.session.add(user) db.session.commit() - transaction = Transaction(user_id=int(user.pid), description='Registered account bonus', value=current_app.config['REGISTER_BONUS']) - db.session.add(transaction) - db.session.commit() + #transaction = Transaction(user_id=int(user.pid), description='Registered account bonus', value=current_app.config['REGISTER_BONUS']) + #db.session.add(transaction) + #db.session.commit() token = user.generate_confirmation_token() send_email(user.email, 'Потвърдете Вашата регистрация', 'auth/email/confirm', user=user, token=token) #notify admin diff --git a/app/main/routes.py b/app/main/routes.py index a44e0b7..7390c86 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -1,6 +1,18 @@ from flask import render_template, abort, redirect, url_for, abort, flash, request, current_app, make_response, g +from flask_login import login_required, login_user, logout_user, current_user +from flask_sqlalchemy import get_debug_queries from . import main +from .. import db +from ..email import send_email +from ..models import User, Permission, Deployment, Service, Region, Address, Domain + +@main.after_app_request +def after_request(response): + for query in get_debug_queries(): + if query.duration >= current_app.config['SLOW_DB_QUERY_TIME']: + current_app.logger.warning('Slow query: %s\nParameters: %s\nDuration: %fs\nContext: %s\n' % (query.statement, query.parameters, query.duration, query.context)) + return response #STATIC PAGES @main.route("/", methods=['GET']) @@ -18,3 +30,69 @@ def chat(): @main.route("/terms", methods=['GET']) def terms(): return render_template('main/terms.html') + +#DASHBOARD +@main.route("/dashboard", methods=['GET', 'POST']) +@login_required +def dashboard(): + cuser = current_user + + inv_deployments = cuser.inv_deployments.order_by(Deployment.date_created.desc()).all() + inv_deploycubeids = [] + inv_deployments_list = [] + for invcls in inv_deployments: + if invcls.user_id == cuser.pid and invcls.enabled == True: + inv_deploycubeids.extend([invcls.machine_id]) + inv_deployments_list.extend([invcls.machine_alias]) + + inv_services = cuser.inv_services.order_by(Service.date_last_charge.asc()).all() + inv_services_list = [] + for invcls in inv_services: + if invcls.user_id == cuser.pid and invcls.enabled == True: + inv_services_list.extend([invcls.description]) + + inv_domains = cuser.inv_domains.order_by(Domain.date_created.desc()).all() + inv_domains_list = [] + for invcls in inv_domains: + if invcls.user_id == cuser.pid and invcls.enabled == True: + inv_domains_list.extend([invcls.fqdn]) + + inv_addresses = cuser.inv_addresses.order_by(Address.ip.asc()).all() + inv_addresses_list = [] + for invcls in inv_addresses: + if invcls.user_id == cuser.pid and invcls.enabled == True: + inv_addresses_list.extend([invcls.ip]) + + sys_regions = Region.query.all() + regions = {} + for region in sys_regions: + regions[region.pid] = region.description + + #extract rrd and status from the deployments + rrd = {} + statuses = {} + for cubeid in inv_deploycubeids: + rrd[cubeid] = {} + try: + query = contact_proxmaster({}, 'vmrrd', cubeid) + except: + flash('Deploy #{} unreachable. Support is notified'.format(str(cubeid))) + send_email(current_app.config['MAIL_USERNAME'], 'Cube {} is unreachable'.format(cubeid), + 'vmanager/email/adm_unreachable', user=current_user, cubeid=cubeid) + + graphs_list = ['net', 'cpu', 'mem', 'hdd'] + try: + for graph in graphs_list: + raw = query[graph]['image'].encode('raw_unicode_escape') + rrd[cubeid][graph] = base64.b64encode(raw).decode() + status = { cubeid : query['status'] } + statuses.update(status) + except Exception as e: + current_app.logger.error(e) + flash('Deploy #{} unreachable. Support is notified'.format(str(cubeid))) + send_email(current_app.config['MAIL_USERNAME'], 'Cube {} is unreachable'.format(cubeid), + 'vmanager/email/adm_unreachable', user=current_user, cubeid=cubeid ) + + current_app.logger.info('[{}] Enabled deployments: {}, services: {}, domains: {}, addresses: {}'.format(current_user.email, inv_deployments_list, inv_services_list, inv_domains_list, inv_addresses_list )) + return render_template('main/dashboard.html', rrd=rrd, status=statuses, inv_deployments=inv_deployments, inv_services=inv_services, inv_domains=inv_domains, inv_addresses=inv_addresses, region=regions) + diff --git a/app/models.py b/app/models.py index c7ae8f7..28438af 100644 --- a/app/models.py +++ b/app/models.py @@ -24,7 +24,7 @@ class Permission: class Role(db.Model): __tablename__ = 'roles' pid = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(64), unique=True) + name = db.Column(db.String, unique=True) default = db.Column(db.Boolean, default=False, index=True) permissions = db.Column(db.Integer) users = db.relationship('User', backref='role', lazy='dynamic') @@ -52,37 +52,36 @@ class User(db.Model, UserMixin): __tablename__ = 'users' pid = db.Column(db.Integer, primary_key=True) - email = db.Column(db.String(64), unique=True, index=True) + email = db.Column(db.String, unique=True, index=True) role_id = db.Column(db.Integer, db.ForeignKey('roles.pid')) #FK - password_hash = db.Column(db.String(128)) + password_hash = db.Column(db.String) tokens = db.Column(db.Text) confirmed = db.Column(db.Boolean, default=False) active = db.Column(db.Boolean, default=True) member_since = db.Column(db.DateTime(), default=datetime.utcnow) last_seen = db.Column(db.DateTime(), default=datetime.utcnow) - last_ip = db.Column(db.String(128)) + last_ip = db.Column(db.String) twofactor = db.Column(db.Boolean, default=False) #optional 2factor auth - otp_secret = db.Column(db.String(16)) - avatar_hash = db.Column(db.String(32)) + otp_secret = db.Column(db.String) + avatar_hash = db.Column(db.String) - name = db.Column(db.Unicode(256)) - address = db.Column(db.Unicode(256)) - city = db.Column(db.Unicode(64)) - postcode = db.Column(db.String(10)) - country = db.Column(db.String(64), default='BG') - phone = db.Column(db.String(64)) + name = db.Column(db.Unicode) + address = db.Column(db.Unicode) + city = db.Column(db.Unicode) + postcode = db.Column(db.String) + country = db.Column(db.String, default='BG') + phone = db.Column(db.String) org_account = db.Column(db.Boolean, default=False) - org_companyname = db.Column(db.Unicode(64)) - org_regaddress = db.Column(db.Unicode(128)) - org_responsible = db.Column(db.Unicode(128)) - org_vatnum = db.Column(db.String(16)) + org_companyname = db.Column(db.Unicode) + org_regaddress = db.Column(db.Unicode) + org_responsible = db.Column(db.Unicode) + org_vatnum = db.Column(db.String) - group = db.Column(db.String(24), default='User') - language = db.Column(db.String(2), default='BG') + group = db.Column(db.String, default='User') + language = db.Column(db.String, default='BG') wallet = db.Column(db.Float) - overdraft = db.Column(db.Float) - currency = db.Column(db.String(3), default='BGN') + currency = db.Column(db.String, default='BGN') inv_deployments = db.relationship('Deployment', backref='owner', lazy='dynamic') inv_services = db.relationship('Service', backref='owner', lazy='dynamic') @@ -232,10 +231,12 @@ class Service(db.Model): date_created = db.Column(db.DateTime, index=True, default=datetime.utcnow) date_last_charge = db.Column(db.DateTime) period = db.Column(db.Integer) + daysleft = db.Column(db.Integer) + warning = db.Column(db.Boolean, default=False) enabled = db.Column(db.Boolean, default=False) - category = db.Column(db.String(64)) - description = db.Column(db.Unicode(128)) + category = db.Column(db.String) + description = db.Column(db.Unicode) price = db.Column(db.Float) class Deployment(db.Model): @@ -245,6 +246,8 @@ class Deployment(db.Model): date_created = db.Column(db.DateTime, index=True, default=datetime.utcnow) date_last_charge = db.Column(db.DateTime) period = db.Column(db.Integer) + daysleft = db.Column(db.Integer) + warning = db.Column(db.Boolean, default=False) enabled = db.Column(db.Boolean, default=False) machine_id = db.Column(db.BigInteger) #cubeid @@ -260,8 +263,8 @@ class Region(db.Model): pid = db.Column(db.Integer, primary_key=True) enabled = db.Column(db.Boolean) - name = db.Column(db.String(64)) - description = db.Column(db.String(128)) + name = db.Column(db.String) + description = db.Column(db.String) extraprice = db.Column(db.Float) class Address(db.Model): @@ -274,9 +277,9 @@ class Address(db.Model): region_id = db.Column(db.Integer, db.ForeignKey('regions.pid')) #FK deploy_id = db.Column(db.Integer, db.ForeignKey('deployments.pid')) #FK - ip = db.Column(db.String(64)) - mac = db.Column(db.String(128)) - rdns = db.Column(db.String(256)) + ip = db.Column(db.String) + mac = db.Column(db.String) + rdns = db.Column(db.String) reserved = db.Column(db.Boolean, default=False) #this ip SHOULD NOT be listed as available to assign even if its not currently owned by anyone def __init__(self): @@ -301,6 +304,8 @@ class Address(db.Model): class Domain(db.Model): __tablename__ = 'domains' pid = db.Column(db.Integer, primary_key=True) + daysleft = db.Column(db.Integer) + warning = db.Column(db.Boolean, default=False) enabled = db.Column(db.Boolean, default=False) user_id = db.Column(db.Integer, db.ForeignKey('users.pid')) #FK @@ -327,7 +332,7 @@ class Invoice(db.Model): date_created = db.Column(db.DateTime, index=True, default=datetime.utcnow) currency = db.Column(db.String, default='BGN') tax = db.Column(db.Float) #VAT - description = db.Column(db.Unicode(255)) + description = db.Column(db.Unicode) def sub_total(self): items = self.items @@ -352,7 +357,7 @@ class InvoiceItem(db.Model): pid = db.Column(db.Integer, primary_key=True) item_number = db.Column(db.Integer) invoice_id = db.Column(db.Integer, db.ForeignKey('invoice.pid')) #FK - item_title = db.Column(db.Unicode(255)) + item_title = db.Column(db.Unicode) item_quantity = db.Column(db.Float) item_price = db.Column(db.Float) amount = db.Column(db.Float) diff --git a/app/templates/admin/list_items.html b/app/templates/admin/list_items.html index 37817d3..b4391d6 100644 --- a/app/templates/admin/list_items.html +++ b/app/templates/admin/list_items.html @@ -23,8 +23,8 @@ Mem HDD Price - Period Last Charged + Days Left @@ -37,8 +37,8 @@ {{ deploy.machine_mem }} MB {{ deploy.machine_hdd }} GB {{ deploy.price }} - {{ deploy.period }} {{ moment(deploy.date_last_charge).format('lll') }} ({{ moment(deploy.date_last_charge).fromNow() }}) + {{ deploy.daysleft }} {% endfor %} @@ -57,8 +57,8 @@ Category Description Price - Period Last Charged + Days Left @@ -68,8 +68,8 @@ {{ service.category }} {{ service.description }} {{ service.price }} - {{ service.period }} {{ moment(service.date_last_charge).format('ll') }} ({{ moment(service.date_last_charge).fromNow() }}) + {{ service.daysleft }} {% endfor %} @@ -89,6 +89,7 @@ Owner Name Expiry Date + Days Left @@ -97,6 +98,7 @@ {{ domain.owner.email }} {{ domain.fqdn }} {{ domain.date_expire }} + {{ domain.daysleft }} {% endfor %} diff --git a/app/templates/vmanager/dashboard.html b/app/templates/main/dashboard.html similarity index 82% rename from app/templates/vmanager/dashboard.html rename to app/templates/main/dashboard.html index 1400807..b84386b 100644 --- a/app/templates/vmanager/dashboard.html +++ b/app/templates/main/dashboard.html @@ -139,6 +139,7 @@ addEventListener("DOMContentLoaded", function() { Network Control + Time Left @@ -150,10 +151,14 @@ addEventListener("DOMContentLoaded", function() { {{ deploy.machine_mem }} MB {{ deploy.machine_hdd }} GB {% for addr in deploy.machine_addresses %} {{ addr.ip }}
{% endfor %} - + {% if deploy.warning == True %} + + {% else %} + {{ deploy.daysleft }} day(s) + {% endif %} + {% endif %} {% endif %} @@ -174,7 +185,7 @@ addEventListener("DOMContentLoaded", function() { - + @@ -191,24 +202,30 @@ addEventListener("DOMContentLoaded", function() { Category Description - Price - Months Last Charged + Time Left {% for service in inv_services %} - + {% if service.enabled == False %} + + {% else %} + + {% endif %} {{ service.category }} {{ service.description }} - {{ service.price }} - {{ service.period }} {{ moment(service.date_last_charge).format('lll') }} ({{ moment(service.date_last_charge).fromNow() }}) - + {% if service.warning == True %} + + {% else %} + {{ service.daysleft }} day(s) + {% endif %} + {% endfor %} - + @@ -225,20 +242,25 @@ addEventListener("DOMContentLoaded", function() { Name Expiry Date - Control + Time Left {% for domain in inv_domains %} {{ domain.fqdn }} {{ domain.date_expire }} - soon... + {% if domain.warning == True %} + + {% else %} + {{ domain.daysleft }} day(s) + {% endif %} + {% endfor %} - + @@ -257,7 +279,6 @@ addEventListener("DOMContentLoaded", function() { Region MAC Addr. Reverse DNS - Control {% for address in inv_addresses %} @@ -266,13 +287,12 @@ addEventListener("DOMContentLoaded", function() { {{ region[address.region_id] }} {{ address.mac }} {{ address.rdns }} - soon... {% endfor %} - + diff --git a/app/templates/nav.html b/app/templates/nav.html index 8b3124f..38bd163 100644 --- a/app/templates/nav.html +++ b/app/templates/nav.html @@ -11,7 +11,7 @@ {% if not current_user.is_authenticated %} {% else %} - + {% endif %} @@ -49,7 +49,7 @@