expand admin page

This commit is contained in:
deflax 2017-06-06 16:49:32 +03:00
parent 16f73f33c2
commit a29ffc0133
12 changed files with 202 additions and 36 deletions

View file

@ -8,6 +8,6 @@ from wtforms import validators, ValidationError
from wtforms.fields.html5 import EmailField from wtforms.fields.html5 import EmailField
class ChargeForm(FlaskForm): class ChargeForm(FlaskForm):
amount = DecimalField('Стойност:', [validators.DataRequired(), validators.NumberRange(min=0, max=6)]) amount = DecimalField('Стойност:', [validators.DataRequired(), validators.NumberRange(min=1, max=500)])
submit = SubmitField('Зареди') submit = SubmitField('Зареди')

View file

@ -7,7 +7,7 @@ from .forms import ChargeForm
from .. import db from .. import db
from ..email import send_email from ..email import send_email
from ..models import User, Role, Deployment, Service, Region, Address, Domain, contact_proxmaster from ..models import User, Deployment, Service, Region, Address, Domain, contact_proxmaster
from ..decorators import admin_required, permission_required from ..decorators import admin_required, permission_required
import base64 import base64
@ -31,6 +31,16 @@ def after_request(response):
@admin.route("/", methods=['GET']) @admin.route("/", methods=['GET'])
@login_required @login_required
@admin_required @admin_required
def index():
alldeployments = Deployment.query.order_by(Deployment.date_created.desc()).all()
alldomains = Domain.query.order_by(Domain.date_created.desc()).all()
allservices = Service.query.order_by(Service.date_created.desc()).all()
alladdresses = Address.query.order_by(Address.date_assigned.desc()).all()
return render_template('admin/index.html', deployments=alldeployments, domains=alldomains, services=allservices, addresses=alladdresses)
@admin.route("/users", methods=['GET'])
@login_required
@admin_required
def users(): def users():
allusers = User.query.order_by(User.pid.asc()).all() allusers = User.query.order_by(User.pid.asc()).all()
return render_template('admin/users.html', users=allusers) return render_template('admin/users.html', users=allusers)
@ -42,7 +52,7 @@ def charge(user_pid=0):
cuser = User.query.filter_by(pid=user_pid).first() cuser = User.query.filter_by(pid=user_pid).first()
form = ChargeForm() form = ChargeForm()
if form.validate_on_submit(): if form.validate_on_submit():
cuser.wallet += form.amount.data cuser.wallet += float(form.amount.data)
db.session.add(cuser) db.session.add(cuser)
db.session.commit() db.session.commit()
return redirect(url_for('admin.users')) return redirect(url_for('admin.users'))

View file

@ -160,7 +160,10 @@ class User(db.Model, UserMixin):
return self.role is not None and (self.role.permissions & permissions) == permissions return self.role is not None and (self.role.permissions & permissions) == permissions
def is_administrator(self): def is_administrator(self):
return self.can(Permission.ADMINISTER) if self.can(Permission.ADMINISTER):
return True
else:
return False
def ping(self): def ping(self):
self.last_seen = datetime.utcnow() self.last_seen = datetime.utcnow()
@ -288,6 +291,7 @@ class Address(db.Model):
ip = db.Column(db.String(64)) ip = db.Column(db.String(64))
rdns = db.Column(db.String(256)) rdns = db.Column(db.String(256))
macaddr = db.Column(db.String(128)) macaddr = db.Column(db.String(128))
reserved = db.Column(db.Boolean, default=False)
class Domain(db.Model): class Domain(db.Model):
__tablename__ = 'domains' __tablename__ = 'domains'

View file

@ -0,0 +1,10 @@
<div class="col-md-12">
<div class="panel panel-warning" id="prxadmin">
<div class="panel-heading">Admin Pages</div>
<div class="panel-body">
<button class="btn btn-default" onclick="window.open('{{ url_for('admin.index') }}','_self')"><span class="glyphicon glyphicon-list" aria-hidden="true"></span> Item Index</button>
<button class="btn btn-default" onclick="window.open('{{ url_for('admin.users') }}','_self')"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> List All Users</button>
</div>
</div>
</div>

View file

@ -4,21 +4,18 @@
{% block page_content %} {% block page_content %}
<div class="container-fluid"> <div class="container-fluid">
<br />
<div class="row"> <div class="row">
{% include "admin/admin_tasks.html" %}
{% block sidebar %} <div class="col-md-12">
{% include "/settings/acc_avatar.html" %}
{% endblock %}
<div class="col-md-8">
<div class="panel panel-info"> <div class="panel panel-info">
<div class="panel-heading">Зареждане на сметка</div> <div class="panel-heading">Зареждане на сметка</div>
<div class="panel-body"> <div class="panel-body">
<form method="POST" action="{{ url_for('admin.charge', user_pid=usr.pid) }}"> <form method="POST" action="{{ url_for('admin.charge', user_pid=usr.pid) }}">
<p> <p>
{{ form.charge_amount.label }} {{ form.charge_amount }}<br /> Current Value: {{ usr.wallet }}<br />
{% for error in form.charge_amount.errors %} {{ form.amount.label }} {{ form.amount }}<br />
{% for error in form.amount.errors %}
{{ error }}<br /> {{ error }}<br />
{% endfor %} {% endfor %}
</p> </p>
@ -35,7 +32,6 @@
</div> </div>
</div> </div>
</div> </div>

View file

@ -105,7 +105,7 @@ addEventListener("DOMContentLoaded", function() {
request.open("GET", "/vmanager/" + command + "/" + vmid, true); request.open("GET", "/vmanager/" + command + "/" + vmid, true);
// and then we send it off // and then we send it off
request.send(); request.send();
//alert("command " + command + " executed."); alert("command " + command + " executed.");
window.location.reload(); window.location.reload();
} }
}); });
@ -118,8 +118,7 @@ addEventListener("DOMContentLoaded", function() {
<div class="container-fluid"> <div class="container-fluid">
<br /> <br />
<div class="row"> <div class="row">
{% include "admin/admin_tasks.html" %}
<a href="{{ url_for('admin.users') }}">Return to user list</a><br />
<div class="col-md-12"> <div class="col-md-12">
<div class="panel panel-danger" id="deployments"> <div class="panel panel-danger" id="deployments">
@ -241,13 +240,10 @@ addEventListener("DOMContentLoaded", function() {
</tbody> </tbody>
</table> </table>
<button class="btn btn-default" onclick="window.open('{{ url_for('main.index') }}')"><span class="glyphicon glyphicon-plus" aria-hiddent="true"></span> Order</button> <button class="btn btn-default" onclick="window.open('{{ url_for('main.index') }}')"><span class="glyphicon glyphicon-plus" aria-hiddent="true"></span> Order</button>
</div> </div>
</div> </div>
</div> </div>
<a href="{{ url_for('admin.users') }}">Return to user list</a><br />
</div> </div>

View file

@ -0,0 +1,132 @@
{% extends "base.html" %}
{% block styles %}
{{ super() }}
</style>
{% endblock %}
{% block page_content %}
<div class="container-fluid">
<br />
<div class="row">
{% include "admin/admin_tasks.html" %}
<div class="col-md-12">
<div class="panel panel-danger" id="deployments">
<div class="panel-heading">Deployments</div>
<div class="panel-body"><p>
<table class="table table-hover table-striped table-condensed cf">
<thead>
<tr>
<th>Cube ID</th>
<th>Alias</th>
<th>CPU</th>
<th>Mem</th>
<th>HDD</th>
<th>Owner</th>
</tr>
<tbody>
{% for deploy in deployments %}
<tr>
<td>{% if deploy.enabled == True %}<font color="green">{% else %}<font color="red">{% endif %}{{ deploy.machine_id }}</td>
<td>{{ deploy.machine_alias }}</font></td>
<td>{{ deploy.machine_cpu }} Cores</td>
<td>{{ deploy.machine_mem }} MB</td>
<td>{{ deploy.machine_hdd }} GB</td>
<td><a href="{{ url_for('admin.dashboard', user_pid=deploy.user_id) }}">{{ deploy.owner.email }}</a></td>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-12">
<div class="panel panel-danger" id="domains">
<div class="panel-heading">Domains</div>
<div class="panel-body"><p>
<table class="table table-hover table-striped table-condensed cf">
<thead>
<tr>
<th>Name</th>
<th>Expiry Date</th>
<th>Owner</th>
</tr>
<tbody>
{% for domain in domains %}
<tr>
<td><b><a href="http://{{ domain.fqdn }}">{{ domain.fqdn }}</a></b></td>
<td>{{ domain.date_expire }}</td>
<td><a href="{{ url_for('admin.dashboard', user_pid=domain.user_id) }}">{{ domain.owner.email }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-12">
<div class="panel panel-danger" id="addresses">
<div class="panel-heading">Addresses</div>
<div class="panel-body"><p>
<table class="table table-hover table-striped table-condensed cf">
<thead>
<tr>
<th>IP</th>
<th>Reverse DNS</th>
<th>MAC Addr.</th>
<th>Owner</th>
</tr>
<tbody>
{% for address in addresses %}
<tr>
<td>{{ address.ip }}</td>
<td>{{ address.rdns }}</td>
<td>{{ address.macaddr }}</td>
<td><a href="{{ url_for('admin.dashboard', user_pid=address.user_id) }}">{{ address.owner.email }}</a></td>
{% endfor %}
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-12">
<div class="panel panel-danger" id="services">
<div class="panel-heading">Services</div>
<div class="panel-body"><p>
<table class="table table-hover table-striped table-condensed cf">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Unit</th>
<th>Price</th>
</tr>
<tbody>
{% for service in services %}
<tr>
<td>{{ service.name }}</td>
<td>{{ service.description }}</td>
<td>{{ service.unit }}</td>
<td>{{ service.unitprice }}</td>
<td><a href="{{ url_for('admin.dashboard', user_pid=service.user_id) }}">{{ service.owner.email }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row">
</div>
</div>
{% endblock %}

View file

@ -2,6 +2,7 @@
{% block page_content %} {% block page_content %}
<div class="row"> <div class="row">
{% include "admin/admin_tasks.html" %}
<div class="col-md-12"> <div class="col-md-12">
<div class="panel panel-danger" id="users"> <div class="panel panel-danger" id="users">
@ -30,11 +31,14 @@
</td> </td>
{% endfor %} {% endfor %}
</tbody> </tbody>
<table> </table>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row">
</div>
{% endblock %} {% endblock %}

View file

@ -24,8 +24,8 @@
{% endif %} {% endif %}
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
<li><a href="#"><span class="glyphicon glyphicon-send"></span> Deploy Application</a></li> <!-- <li><a href="#"><span class="glyphicon glyphicon-send"></span> Deploy Application</a></li>
<!-- <li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-send"></span> Deploy Application</a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-send"></span> Deploy Application</a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="/market/1"><span class="glyphicon glyphicon-send"></span> Business</a></li> <li><a href="/market/1"><span class="glyphicon glyphicon-send"></span> Business</a></li>
@ -43,6 +43,9 @@
<li><a href="{{ url_for('auth.register') }}">Register</a></li> <li><a href="{{ url_for('auth.register') }}">Register</a></li>
<li><a href="{{ url_for('auth.login') }}">Login</a></li> <li><a href="{{ url_for('auth.login') }}">Login</a></li>
{% else %} {% else %}
{% if current_user.is_administrator() %}
<li><a href="{{ url_for('admin.index') }}"><span class="glyphicon glyphicon-wrench"></span> Admin Panel</a></li>
{% endif %}
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><img class="avatar" src="{{ current_user.gravatar(20) }}"> {{ current_user.email }} <span class="caret"></span></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><img class="avatar" src="{{ current_user.gravatar(20) }}"> {{ current_user.email }} <span class="caret"></span></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">

View file

@ -5,6 +5,7 @@
<b>Member Since:</b> {{ moment(current_user.member_since).fromNow() }}<br /> <b>Member Since:</b> {{ moment(current_user.member_since).fromNow() }}<br />
<b>Group:</b> {{ current_user.group }}<br /> <b>Group:</b> {{ current_user.group }}<br />
<b>2-Factor:</b> {{ current_user.twofactor }}<br /> <b>2-Factor:</b> {{ current_user.twofactor }}<br />
<a href="/auth/change-password">Change Password</a>
</p> </p>
</div> </div>
</div> </div>

View file

@ -105,7 +105,7 @@ addEventListener("DOMContentLoaded", function() {
request.open("GET", "/vmanager/" + command + "/" + vmid, true); request.open("GET", "/vmanager/" + command + "/" + vmid, true);
// and then we send it off // and then we send it off
request.send(); request.send();
//alert("command " + command + " executed."); alert("command " + command + " executed.");
window.location.reload(); window.location.reload();
} }
}); });

View file

@ -6,7 +6,7 @@ from . import vmanager
from .forms import DeployForm from .forms import DeployForm
from .. import db from .. import db
from ..email import send_email from ..email import send_email
from ..models import User, Role, Deployment, Service, Region, Address, Domain, contact_proxmaster from ..models import User, Permission, Deployment, Service, Region, Address, Domain, contact_proxmaster
from ..decorators import admin_required, permission_required from ..decorators import admin_required, permission_required
import base64 import base64
@ -66,7 +66,7 @@ def deploy(product_id=None):
try: try:
product = Product.get_products()[product_id] product = Product.get_products()[product_id]
except: except:
print('unknown product {}'.format(product_id)) current_app.logger.error('unknown product {}'.format(product_id))
abort(404) abort(404)
product_pic = '..' + product['img'] product_pic = '..' + product['img']
product_name = product['name'] product_name = product['name']
@ -169,7 +169,7 @@ def dashboard():
status = { cubeid : query['status'] } status = { cubeid : query['status'] }
statuses.update(status) statuses.update(status)
except Exception as e: except Exception as e:
print(e) current_app.logger.error(e)
flash('Deploy #{} unreachable. Support is notified'.format(str(cubeid))) flash('Deploy #{} unreachable. Support is notified'.format(str(cubeid)))
send_email(current_app.config['MAIL_USERNAME'], 'Cube {} is unreachable'.format(cubeid), send_email(current_app.config['MAIL_USERNAME'], 'Cube {} is unreachable'.format(cubeid),
'vmanager/email/adm_unreachable', user=current_user, cubeid=cubeid ) 'vmanager/email/adm_unreachable', user=current_user, cubeid=cubeid )
@ -188,14 +188,19 @@ def vnc(vmid=0):
#checks if current user owns this vmid #checks if current user owns this vmid
if not vmid in inventory: if not vmid in inventory:
print('WARNING: user does not own vmid: ' + str(vmid)) current_app.logger.warning('User does not own cube id: {}'.format(str(vmid)))
#TODO: log ips #TODO: log ips
else: else:
data = {} db_result = contact_proxmaster({}, 'vmvnc', vmid)
db_result = contact_proxmaster(data, 'vmvnc', vmid)
#return render_template('vmanager/vnc.html', url=db_result['url']) #return render_template('vmanager/vnc.html', url=db_result['url'])
return redirect(db_result['url']) return redirect(db_result['url'])
#admin override
if current_user.is_administrator():
current_app.logger.warning('Admin override for cube id: {}'.format(str(vmid)))
db_result = contact_proxmaster({}, 'vmvnc', vmid)
return redirect(db_result['url'])
abort(404) abort(404)
valid_commands = ['vmstatus', 'vmstart', 'vmshutdown', 'vmstop'] valid_commands = ['vmstatus', 'vmstart', 'vmshutdown', 'vmstop']
@ -205,9 +210,8 @@ valid_commands = ['vmstatus', 'vmstart', 'vmshutdown', 'vmstop']
def command(cmd=None, vmid=0): def command(cmd=None, vmid=0):
#checks whether this is a valid command #checks whether this is a valid command
if not cmd in valid_commands: if not cmd in valid_commands:
print('WARNING: ' + cmd + ' is not a valid command!') current_app.logger.warning(cmd + ' is not a valid command!')
abort(404) abort(404)
#if cmd == 'vmstart' and current_user.wallet < 3.0: #if cmd == 'vmstart' and current_user.wallet < 3.0:
# flash('Недостатъчно средства в сметката за тази операция') # flash('Недостатъчно средства в сметката за тази операция')
# return redirect(url_for('uinvoice.addfunds')) # return redirect(url_for('uinvoice.addfunds'))
@ -218,13 +222,19 @@ def command(cmd=None, vmid=0):
if invcls.enabled == True: if invcls.enabled == True:
inventory.extend([invcls.machine_id]) inventory.extend([invcls.machine_id])
#checks if current user owns this vmid #checks if current user owns this vmid
if not vmid in inventory: if not vmid in inventory:
print('WARNING: user id:{} does not own cube id:{}'.format(current_user.pid, vmid)) current_app.logger.warning('user id: {} does not own cube id:{}'.format(current_user.pid, vmid))
#TODO: log ips
else: else:
data = {} db_result = contact_proxmaster({}, cmd, vmid)
db_result = contact_proxmaster(data, cmd, vmid)
#print(db_result) #print(db_result)
#TODO: log ips
#admin override
if current_user.is_administrator():
current_app.logger.warning('Admin override for cube id:{}'.format(vmid))
db_result = contact_proxmaster({}, cmd, vmid)
abort(404) abort(404)