ingegrate support back to the panel

This commit is contained in:
deflax 2018-04-23 01:00:05 +03:00
parent 2c435e6b2b
commit c230ddcfc8
11 changed files with 102 additions and 136 deletions

View file

@ -68,9 +68,6 @@ app.register_blueprint(smanager_blueprint, url_prefix='/smanager')
from .dmanager import dmanager as dmanager_blueprint from .dmanager import dmanager as dmanager_blueprint
app.register_blueprint(dmanager_blueprint, url_prefix='/dmanager') app.register_blueprint(dmanager_blueprint, url_prefix='/dmanager')
from .livesupport import livesupport as livesupport_blueprint
app.register_blueprint(livesupport_blueprint, url_prefix='/support')
from .uinvoice import uinvoice as uinvoice_blueprint from .uinvoice import uinvoice as uinvoice_blueprint
app.register_blueprint(uinvoice_blueprint, url_prefix='/uinvoice') app.register_blueprint(uinvoice_blueprint, url_prefix='/uinvoice')

View file

@ -1,3 +0,0 @@
from flask import Blueprint
livesupport = Blueprint('livesupport', __name__)
from . import routes

View file

@ -1,27 +0,0 @@
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField, SelectField, DecimalField
from flask_pagedown.fields import PageDownField
from wtforms import validators, ValidationError
from wtforms.fields.html5 import EmailField, DecimalRangeField
from .. import db
class OrderForm(FlaskForm):
region_choices = [(1, 'Plovdiv, Bulgaria'), (2, 'International Space Station')]
region = SelectField('Region:', choices=region_choices, coerce=int)
recipe_choices = [(1, 'RootVPS')]
recipe = SelectField('Type:', choices=recipe_choices, coerce=int)
cpu = DecimalRangeField('Processor Cores', default=2)
memory = DecimalRangeField('Memory', default=2048)
storage = DecimalRangeField('Storage', default=20)
alias = StringField('Name:', [validators.Regexp(message='ex.: myservice1.com, myservice2.local', regex='^[a-zA-Z0-9][a-zA-Z0-9-_]{0,61}[a-zA-Z0-9]{0,1}\.([a-zA-Z]{1,6}|[a-zA-Z0-9-]{1,30}\.[a-zA-Z]{2,3})$'), validators.Length(6,64)])
submit = SubmitField('DEPLOY')
class MessageForm(FlaskForm):
line = PageDownField('Enter your message...', validators=[validators.DataRequired()])
submit = SubmitField('Submit')

View file

@ -1,83 +0,0 @@
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 livesupport
from .forms import MessageForm
from .. import db
from ..email import send_email
from ..models import User, Permission, SupportTopic, SupportLine, contact_proxmaster
import base64
from datetime import date, time, datetime
from dateutil.relativedelta import relativedelta
@livesupport.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
#SUPPORT
@livesupport.route("/support", methods=['GET'])
@login_required
def support_list():
""" general enquiry and list all open support tasks """
cuser = current_user
form = MessageForm()
alltopics = cuser.inv_topics.all()
return render_template('livesupport/support_list.html', form=form, inv_topics=alltopics)
@livesupport.route("/support/<string:topic>/", methods=['GET', 'POST'])
@login_required
def support(topic):
""" block item for support chatbox. invoked from vdc_pool or supportlist """
cuser = current_user
form = MessageForm()
if request.method == "GET":
support_topic = SupportTopic.query.filter_by(hashtag=str(topic)).first()
if support_topic == None:
class EmptySupport():
hashtag=str(topic)
timestamp=datetime.utcnow()
support_topic = EmptySupport()
return render_template('livesupport/support_item.html', form=form, support=support_topic)
else:
if support_topic.user_id != cuser.pid:
abort(403) #TODO: hidden 403. there is a topic like that but its not yours!
else:
#topic is yours. show it.
return render_template('livesupport/support_item.html', form=form, support=support_topic)
if request.method == "POST" and form.validate_on_submit():
support_topic = SupportTopic.query.filter_by(hashtag=str(topic)).first()
if support_topic == None:
#no topic. create one?
if cuser.inv_topics.all() != []:
#check if other topics exist, and ratelimit
last_topic = cuser.inv_topics.order_by(SupportTopic.timestamp.desc()).first()
now = datetime.utcnow()
time_last_topic = last_topic.timestamp
expiry = time_last_topic + relativedelta(time_last_topic, minutes=+5)
if now < expiry:
flash('ratelimit. try again later')
return redirect(url_for('livesupport.support_list'))
#create new topic
new_topic = SupportTopic(user_id=cuser.pid, hashtag=str(topic))
db.session.add(new_topic)
new_line = SupportLine(topic_id=new_topic.pid, line=str(form.line.data))
db.session.add(new_line)
else:
if support_topic.user_id == cuser.pid:
new_line = SupportLine(topic_id=support_topic.pid, line=form.line.data)
db.session.add(new_line)
else:
abort(403) #TODO: hidden 404
db.session.commit()
return redirect(url_for('livesupport.support_list'))

View file

@ -88,7 +88,6 @@ class User(db.Model, UserMixin):
currency = db.Column(db.String, default='BGN') currency = db.Column(db.String, default='BGN')
inv_transactions = db.relationship('Transaction', backref='owner', lazy='dynamic') inv_transactions = db.relationship('Transaction', backref='owner', lazy='dynamic')
inv_orders = db.relationship('Order', backref='owner', lazy='dynamic') inv_orders = db.relationship('Order', backref='owner', lazy='dynamic')
inv_topics = db.relationship('SupportTopic', backref='owner', lazy='dynamic')
inv_servers = db.relationship('Server', backref='owner', lazy='dynamic') inv_servers = db.relationship('Server', backref='owner', lazy='dynamic')
inv_deployments = db.relationship('Deployment', backref='owner', lazy='dynamic') inv_deployments = db.relationship('Deployment', backref='owner', lazy='dynamic')
@ -99,6 +98,7 @@ class User(db.Model, UserMixin):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(User, self).__init__(**kwargs) super(User, self).__init__(**kwargs)
if self.role is None: if self.role is None:
if self.email == current_app.config['ADMIN_EMAIL']: if self.email == current_app.config['ADMIN_EMAIL']:
#if email match config admin name create admin user #if email match config admin name create admin user
@ -110,6 +110,7 @@ class User(db.Model, UserMixin):
if self.avatar_hash is None and self.email is not None: if self.avatar_hash is None and self.email is not None:
self.avatar_hash = hashlib.md5(self.email.encode('utf-8')).hexdigest() self.avatar_hash = hashlib.md5(self.email.encode('utf-8')).hexdigest()
if self.otp_secret is None: if self.otp_secret is None:
# generate a random secret # generate a random secret
self.otp_secret = base64.b32encode(os.urandom(10)).decode('utf-8') self.otp_secret = base64.b32encode(os.urandom(10)).decode('utf-8')
@ -283,6 +284,7 @@ class Deployment(db.Model):
pid = db.Column(db.Integer, primary_key=True) pid = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.ForeignKey('users.pid')) #FK user_id = db.Column(db.ForeignKey('users.pid')) #FK
server_id = db.Column(db.ForeignKey('servers.pid')) #FK server_id = db.Column(db.ForeignKey('servers.pid')) #FK
topic_id = db.Column(db.ForeignKey('support_topic.pid')) #FK
date_created = db.Column(db.DateTime, default=datetime.utcnow) date_created = db.Column(db.DateTime, default=datetime.utcnow)
deleted = db.Column(db.Boolean, default=False) deleted = db.Column(db.Boolean, default=False)
@ -302,6 +304,16 @@ class Deployment(db.Model):
inv_pubvlans = db.relationship('PubVLAN', backref='deploy', lazy='dynamic') inv_pubvlans = db.relationship('PubVLAN', backref='deploy', lazy='dynamic')
def __init__(self, **kwargs):
super(Deployment, self).__init__(**kwargs)
if self.topic_id is None:
#create new topic
new_topic = SupportTopic(hashtag='deploy-' + query['unit_id'])
db.session.add(new_topic)
db.session.commit()
self.topic_id = new_topic.pid
class PubVLAN(db.Model): class PubVLAN(db.Model):
__tablename__ = 'pubvlans' __tablename__ = 'pubvlans'
pid = db.Column(db.Integer, primary_key=True) pid = db.Column(db.Integer, primary_key=True)
@ -433,14 +445,15 @@ class InvoiceItem(db.Model):
class SupportTopic(db.Model): class SupportTopic(db.Model):
__tablename__ = 'support_topic' __tablename__ = 'support_topic'
pid = db.Column(db.Integer, primary_key=True) pid = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.ForeignKey('users.pid')) #fk
hashtag = db.Column(db.String) #topic hashtag = db.Column(db.String) #topic
timestamp = db.Column(db.DateTime(), default=datetime.utcnow) timestamp = db.Column(db.DateTime(), default=datetime.utcnow)
inv_lines = db.relationship('SupportLine', backref='topic', lazy='dynamic') inv_lines = db.relationship('SupportLine', backref='topic', lazy='dynamic')
inv_deployments = db.relationship('Deployment', backref='topic', lazy='dynamic')
class SupportLine(db.Model): class SupportLine(db.Model):
__tablename__ = 'support_line' __tablename__ = 'support_line'
pid = db.Column(db.Integer, primary_key=True) pid = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.ForeignKey('users.pid')) #FK
topic_id = db.Column(db.ForeignKey('support_topic.pid')) #FK topic_id = db.Column(db.ForeignKey('support_topic.pid')) #FK
line = db.Column(db.Unicode) line = db.Column(db.Unicode)
timestamp = db.Column(db.DateTime(), default=datetime.utcnow) timestamp = db.Column(db.DateTime(), default=datetime.utcnow)

View file

@ -21,4 +21,7 @@ class OrderForm(FlaskForm):
submit = SubmitField('DEPLOY') submit = SubmitField('DEPLOY')
class MessageForm(FlaskForm):
line = PageDownField('Enter your message...', validators=[validators.DataRequired()])
submit = SubmitField('Submit')

View file

@ -3,10 +3,10 @@ from flask_login import login_required, login_user, logout_user, current_user
from flask_sqlalchemy import get_debug_queries from flask_sqlalchemy import get_debug_queries
from . import panel from . import panel
from .forms import OrderForm from .forms import OrderForm, MessageForm
from .. import db from .. import db
from ..email import send_email from ..email import send_email
from ..models import User, Permission, Recipe, Order, Server, Deployment, Service, Region, Address, Domain, contact_proxmaster from ..models import User, Permission, Recipe, Order, Server, Deployment, Service, Region, Address, Domain, SupportTopic, SupportLine, contact_proxmaster
import base64 import base64
from datetime import date, time, datetime from datetime import date, time, datetime
@ -99,3 +99,66 @@ def dashboard(user_pid):
continue continue
return render_template('panel/dashboard.html', sys_regions=sys_regions, inv_deployments=inv_deployments, inv_services=inv_services, inv_domains=inv_domains, inv_addresses=inv_addresses, rrd=rrd, status=statuses, warnflag=warnflag, regions=regions) return render_template('panel/dashboard.html', sys_regions=sys_regions, inv_deployments=inv_deployments, inv_services=inv_services, inv_domains=inv_domains, inv_addresses=inv_addresses, rrd=rrd, status=statuses, warnflag=warnflag, regions=regions)
#SUPPORT
@panel.route("/list", methods=['GET'])
@login_required
def support_list():
""" general enquiry and list all open support tasks """
cuser = current_user
form = MessageForm()
alltopics = cuser.inv_topics.all()
return render_template('panel/support_list.html', form=form, inv_topics=alltopics)
@panel.route("/topic/<string:topic>/", methods=['GET', 'POST'])
@login_required
def support(topic):
""" block item for support chatbox. invoked from vdc_pool or supportlist """
cuser = current_user
form = MessageForm()
if request.method == "GET":
support_topic = SupportTopic.query.filter_by(hashtag=str(topic)).first()
if support_topic == None:
class EmptySupport():
hashtag=str(topic)
timestamp=datetime.utcnow()
support_topic = EmptySupport()
return render_template('panel/support_item.html', form=form, support=support_topic)
else:
if support_topic.user_id != cuser.pid:
abort(403) #TODO: hidden 403. there is a topic like that but its not yours!
else:
#topic is yours. show it.
return render_template('panel/support_item.html', form=form, support=support_topic)
if request.method == "POST" and form.validate_on_submit():
support_topic = SupportTopic.query.filter_by(hashtag=str(topic)).first()
if support_topic == None:
#no topic. create one?
if cuser.inv_topics.all() != []:
#check if other topics exist, and ratelimit
last_topic = cuser.inv_topics.order_by(SupportTopic.timestamp.desc()).first()
now = datetime.utcnow()
time_last_topic = last_topic.timestamp
expiry = time_last_topic + relativedelta(time_last_topic, minutes=+5)
if now < expiry:
flash('ratelimit. try again later')
return redirect(url_for('panel.support_list'))
#create new topic
new_topic = SupportTopic(user_id=cuser.pid, hashtag=str(topic))
db.session.add(new_topic)
new_line = SupportLine(topic_id=new_topic.pid, line=str(form.line.data))
db.session.add(new_line)
else:
if support_topic.user_id == cuser.pid:
new_line = SupportLine(topic_id=support_topic.pid, line=form.line.data)
db.session.add(new_line)
else:
abort(403) #TODO: hidden 404
db.session.commit()
return redirect(url_for('panel.support_list'))

View file

@ -52,7 +52,7 @@
<li><a href="{{ url_for('uinvoice.transactions') }}"><span class="glyphicon glyphicon-list-alt"></span> Transactions</a></li> <li><a href="{{ url_for('uinvoice.transactions') }}"><span class="glyphicon glyphicon-list-alt"></span> Transactions</a></li>
<li role="separator" class="divider"></li> <li role="separator" class="divider"></li>
<li><a href="{{ url_for('settings.profile') }}"><span class="glyphicon glyphicon-user"></span> Profile</a></li> <li><a href="{{ url_for('settings.profile') }}"><span class="glyphicon glyphicon-user"></span> Profile</a></li>
<li><a href="{{ url_for('livesupport.support_list') }}"><span class="glyphicon glyphicon-question-sign"></span> Support</a></li> <li><a href="{{ url_for('panel.support_list') }}"><span class="glyphicon glyphicon-question-sign"></span> Support</a></li>
<li><a href="{{ url_for('auth.logout') }}"><span class="glyphicon glyphicon-off"></span> Logout</a></li> <li><a href="{{ url_for('auth.logout') }}"><span class="glyphicon glyphicon-off"></span> Logout</a></li>
</ul> </ul>
</li> </li>

View file

@ -1,16 +1,5 @@
{% block support_item %} {% block support_item %}
{% if support.inv_lines != [] %} {% if support != None %}
<div class="col-sm-3 col-sm-offset-4 frame">
<ul></ul>
<div>
<div class="msj-rta macro">
<div class="text text-r" style="background:whitesmoke !important">
<input class="mytext" placeholder="Type a message"/>
</div>
</div>
<span class="glyphicon glyphicon-share-alt"></span>
</div>
</div>
<div class="panel panel-default" style="margin-top: 0px"> <div class="panel panel-default" style="margin-top: 0px">
<div class="panel-heading" href="#support{{ support.pid }}" aria-expanded="true" aria-controls="support{{ support.pid }}" role="tab" id="support{{ support.pid }}"> <div class="panel-heading" href="#support{{ support.pid }}" aria-expanded="true" aria-controls="support{{ support.pid }}" role="tab" id="support{{ support.pid }}">
@ -25,7 +14,7 @@
{% endfor %} {% endfor %}
{% endif %} {% endif %}
<form method="POST" action="{{ url_for('livesupport.support', topic=support.hashtag) }}"> <form method="POST" action="{{ url_for('panel.support', topic=support.hashtag) }}">
{{ form.line | safe }} {{ form.line | safe }}
{% for error in form.line.errors %} {% for error in form.line.errors %}
{{ error }}<br /> {{ error }}<br />

View file

@ -25,7 +25,7 @@ $('a[data-toggle="tooltip"]').tooltip({
<div class="panel-heading">{{ support.hashtag }}</div> <div class="panel-heading">{{ support.hashtag }}</div>
<div class="panel-body"><p> <div class="panel-body"><p>
<div class="panel-group" id="support{{ support.pid }}" role="tablist" aria-multiselectable="true"> <div class="panel-group" id="support{{ support.pid }}" role="tablist" aria-multiselectable="true">
{% include "livesupport/support_item.html" %} {% include "panel/support_item.html" %}
</div> </div>
</div> </div>
</div> </div>

View file

@ -22,6 +22,7 @@
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#nfo{{ deploy.machine_id }}">Information</a></li> <li class="active"><a data-toggle="tab" href="#nfo{{ deploy.machine_id }}">Information</a></li>
<li><a data-toggle="tab" href="#controls{{ deploy.machine_id }}">Control</a></li> <li><a data-toggle="tab" href="#controls{{ deploy.machine_id }}">Control</a></li>
<li><a data-toggle="tab" href="#support{{ deploy.machine_id }}">Support</a></li>
<li><a data-toggle="tab" href="#graphs{{ deploy.machine_id }}">Monitoring</a></li> <li><a data-toggle="tab" href="#graphs{{ deploy.machine_id }}">Monitoring</a></li>
</ul> </ul>
@ -92,7 +93,20 @@
</div> </div>
</p> </p>
</div> </div>
<div id="support{{ deploy.machine_id }}" class="tab-pane fade">
<p>
<div class="col-md-12">
<div class="panel panel-info" id="supportpanel{{ deploy.machine_id }}">
<div class="panel-heading"></div>
<div class="panel-body"><p>
{% with support=deploy.discussion %}
{% include "panel/support_item.html" %}
{% endwith %}
</div>
</div>
</div>
</p>
</div>
</div> </div>
</div> </div>