separate settings

This commit is contained in:
deflax 2017-03-13 15:36:21 +02:00
parent bfb76658d1
commit 3261c0a96d
11 changed files with 249 additions and 126 deletions

View file

@ -37,6 +37,9 @@ app.register_blueprint(vmanager_blueprint)
from .auth import auth as auth_blueprint from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth') app.register_blueprint(auth_blueprint, url_prefix='/auth')
from .settings import settings as settings_blueprint
app.register_blueprint(settings_blueprint, url_prefix='/settings')
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')
@ -57,7 +60,7 @@ app.json_encoder = CustomJSONEncoder
if not app.debug: if not app.debug:
import logging import logging
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
file_handler = RotatingFileHandler('/home/proxadmin/appserver/proxadmin/app/log/proxadmin.log', 'a', 1 * 1024 * 1024, 10) file_handler = RotatingFileHandler('/home/proxadmin/appserver/proxadmin/log/proxadmin.log', 'a', 1 * 1024 * 1024, 10)
file_handler.setLevel(logging.INFO) file_handler.setLevel(logging.INFO)
file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]')) file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
app.logger.addHandler(file_handler) app.logger.addHandler(file_handler)

View file

@ -56,6 +56,43 @@ def login():
return render_template('auth/login.html', page=page, form=form) return render_template('auth/login.html', page=page, form=form)
#PROFILE
@auth.route('/profile', methods=['GET', 'POST'])
@login_required
def profile():
page = { 'title': 'Edit Profile' }
currentmail = current_user.email
ouruser = User.query.filter_by(email=currentmail).first()
db.session.commit()
wallet = "%.2f" % round(ouruser.wallet, 3)
print(wallet)
form = EditProfileForm()
if form.validate_on_submit():
current_user.name = form.name.data
current_user.address = form.address.data
current_user.city = form.city.data
current_user.postcode = form.postcode.data
current_user.country = form.country.data
current_user.phone = form.phone.data
current_user.org_responsible = form.org_responsible.data
current_user.org_bulstat = form.org_bulstat.data
current_user.twofactor = form.twofactor.data
db.session.add(current_user)
db.session.commit()
flash('Info Updated!')
form.twofactor.data = current_user.twofactor
form.name.data = current_user.name
form.address.data = current_user.address
form.city.data = current_user.city
form.postcode.data = current_user.postcode
form.country.data = current_user.country
form.phone.data = current_user.phone
form.org_responsible.data = current_user.org_responsible
form.org_bulstat.data = current_user.org_bulstat
return render_template('auth/profile.html', page=page, form=form)
@auth.route('/twofactor', methods=['GET', 'POST']) @auth.route('/twofactor', methods=['GET', 'POST'])
def twofactor(): def twofactor():

View file

@ -1,4 +1,3 @@
import hashlib
from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.security import generate_password_hash, check_password_hash
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
@ -9,12 +8,13 @@ from . import db, lm
import os import os
import base64 import base64
import hashlib
from decimal import Decimal
from datetime import date, time, datetime, timedelta
import json import json
from sortedcontainers import SortedDict
import requests import requests
import onetimepass import onetimepass
from datetime import date, datetime, time, timedelta
from sortedcontainers import SortedDict
class Permission: class Permission:
DEPLOY = 0x01 DEPLOY = 0x01
@ -57,6 +57,7 @@ class User(db.Model, UserMixin):
password_hash = db.Column(db.String(128)) password_hash = db.Column(db.String(128))
confirmed = db.Column(db.Boolean, default=False) confirmed = db.Column(db.Boolean, default=False)
active = db.Column(db.Boolean, default=True)
member_since = db.Column(db.DateTime(), default=datetime.utcnow) member_since = db.Column(db.DateTime(), default=datetime.utcnow)
last_seen = 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(128))
@ -70,15 +71,24 @@ class User(db.Model, UserMixin):
postcode = db.Column(db.String(10)) postcode = db.Column(db.String(10))
country = db.Column(db.String(64)) country = db.Column(db.String(64))
phone = db.Column(db.String(64)) phone = db.Column(db.String(64))
org_companyname = db.Column(db.Unicode(64))
org_regaddress = db.Column(db.Unicode(128))
org_responsible = db.Column(db.Unicode(128)) org_responsible = db.Column(db.Unicode(128))
org_bulstat = db.Column(db.String(16)) org_bulstat = db.Column(db.String(16))
org_vat = db.Column(db.Boolean, default=False)
org_vatnum = db.Column(db.String(16))
wallet = db.Column(db.Float)
credit = db.Column(db.Float)
creditlimit = db.Column(db.Float, default=20.0)
currency = db.Column(db.String(3), default='BGN')
language = db.Column(db.String(2), default='BG')
inv_deployments = db.relationship('Deployment', backref='owner', lazy='dynamic') inv_deployments = db.relationship('Deployment', backref='owner', lazy='dynamic')
inv_contracts = db.relationship('Contract', backref='owner', lazy='dynamic') inv_contracts = db.relationship('Contract', backref='owner', lazy='dynamic')
inv_domains = db.relationship('Domain', backref='owner', lazy='dynamic') inv_domains = db.relationship('Domain', backref='owner', lazy='dynamic')
currency = db.Column(db.String(3), default='BGN')
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:
@ -271,7 +281,7 @@ class Service(db.Model):
pid = db.Column(db.Integer, primary_key=True) #PK pid = db.Column(db.Integer, primary_key=True) #PK
name = db.Column(db.String(64)) name = db.Column(db.String(64))
image = db.Column(db.String(128)) image = db.Column(db.String(128))
description = db.Column(db.String(128)) description = db.Column(db.Unicode(128))
unitprice = db.Column(db.Float) unitprice = db.Column(db.Float)
enabled = db.Column(db.Boolean) enabled = db.Column(db.Boolean)
@ -305,17 +315,57 @@ class Service(db.Model):
} }
return services return services
class Order(db.Model): #TRANSACTIONS
__tablename__ = 'orders' class Transaction(db.Model):
__tablename__ = 'transaction'
pid = db.Column(db.Integer, primary_key=True) pid = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.pid')) #FK user_id = db.Column(db.Integer, db.ForeignKey('users.pid')) #FK
date_created = db.Column(db.DateTime, index=True, default=datetime.utcnow) date_created = db.Column(db.DateTime, index=True, default=datetime.utcnow)
units = db.Column(db.Integer, default=1)
unitvalue = db.Column(db.Float)
currency = db.Column(db.String, default='BGN') currency = db.Column(db.String, default='BGN')
paid = db.Column(db.Boolean, default=False)
#class Invoice(db.Model) #PROFORMA INVOICE CLASS
class Invoice(db.Model):
__tablename__ = 'invoice'
pid = db.Column(db.Integer, primary_key=True)
invoice_number = db.Column(db.Integer, unique=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.pid')) #FK
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))
def sub_total(self):
items = self.items
sub_total = 0
for item in items:
sub_total += item.total()
return sub_total
def tax_amount(self):
if not self.tax:
return 0
return self.sub_total() * self.tax / 100.0
def grand_total(self):
amount_str = str(self.sub_total() + self.tax_amount())
amount = Decimal(amount_str)
rounder = Decimal("0.05") # precision for rounding
return amount - amount.remainder_near(rounder)
class InvoiceItem(db.Model):
__tablename__ = 'invoice_item'
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_quantity = db.Column(db.Float)
item_price = db.Column(db.Float)
amount = db.Column(db.Float)
invoice = db.relationship(Invoice, backref=backref('items', order_by=item_number, cascade="delete"))
def total(self):
return self.item_quantity * self.item_price
#INVENTORY CLASSES #INVENTORY CLASSES
class Deployment(db.Model): class Deployment(db.Model):
@ -328,35 +378,34 @@ class Deployment(db.Model):
enabled = db.Column(db.Boolean) enabled = db.Column(db.Boolean)
machine_id = db.Column(db.BigInteger) #cubeid machine_id = db.Column(db.BigInteger) #cubeid
machine_alias = db.Column(db.String) #dns name for easy managing machine_alias = db.Column(db.String) #dns name
machine_cpu = db.Column(db.Integer) machine_cpu = db.Column(db.Integer)
machine_mem = db.Column(db.Integer) machine_mem = db.Column(db.Integer)
machine_hdd = db.Column(db.Integer) machine_hdd = db.Column(db.Integer)
credit = db.Column(db.Float)
def charge(): def charge():
result = Deployment.query.all() result = Deployment.query.all()
for deploy in result: for deploy in result:
managed_user = User.query.get(deploy.user_id) if deploy.enabled == True:
db.session.add(deploy) managed_user = User.query.get(deploy.user_id)
if datetime.utcnow.date() > (deploy.date_expire - timedelta(days=10)): db.session.add(managed_user)
if deploy.credit > 0: current_product = Product.query.get(int(deploy.product_id))
print('{} Deployment {} will expire in 10 days at {}. Creating new order...'.format(managed_user.email, deploy.machine_alias, deploy.date_expire))
current_product = Product.query.get(int(deploy.product_id))
order = Order(user_id=managed_user.pid, unitvalue=deploy.credit, description='Cloud server {} - {}'.format(current_product.name, deploy.machine_alias))
db.session.add(order)
deploy.credit = 0
deploy.data_expire = datetime.utcnow.date() + timedelta(days=30)
else:
cpu_cost = deploy.machine_cpu * current_app.config['CPU_RATIO'] cpu_cost = deploy.machine_cpu * current_app.config['CPU_RATIO']
mem_cost = ( deploy.machine_mem / 1024 ) * current_app.config['MEM_RATIO'] mem_cost = ( deploy.machine_mem / 1024 ) * current_app.config['MEM_RATIO']
hdd_cost = deploy.machine_hdd * current_app.config['HDD_RATIO'] hdd_cost = deploy.machine_hdd * current_app.config['HDD_RATIO']
total = cpu_cost + mem_cost + hdd_cost total = cpu_cost + mem_cost + hdd_cost
if deploy.enabled == True:
print('{}> Charging deployment #{} with {} ({} monthly)'.format(managed_user.email, deploy.pid, total))
deploy.credit += total
db.session.commit() if managed_user.wallet - managed_user.credit > managed_user.creditlimit:
return True print('{}> Deployment #{} costs {} today. Credit is now {}'.format(managed_user.email, deploy.machine_id, total, managed_user.credit))
managed_user.credit += total
else:
print('{}> Deployment #{} costs {} today. Credit now is {} and its lower than the credit limit {}.'.format(managed_user.email, deploy.machine_id, total, managed_user.credit, managed_user.creditlimit)
print('')
if datetime.utcnow.date() ==
db.session.commit()
if deploy.
class Contract(db.Model): class Contract(db.Model):
__tablename__ = 'contracts' __tablename__ = 'contracts'
@ -367,7 +416,7 @@ class Contract(db.Model):
date_expire = db.Column(db.DateTime) date_expire = db.Column(db.DateTime)
enabled = db.Column(db.Boolean) enabled = db.Column(db.Boolean)
description = db.Column(db.String) description = db.Column(db.Unicode)
units = db.Column(db.Integer) units = db.Column(db.Integer)
discount = db.Column(db.Integer) #percent discount = db.Column(db.Integer) #percent
credit = db.Column(db.Float) credit = db.Column(db.Float)

3
app/settings/__init__.py Normal file
View file

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

60
app/settings/forms.py Normal file
View file

@ -0,0 +1,60 @@
from iso3166 import countries
import string
import random
from ..models import User, Role
from flask_wtf import FlaskForm, RecaptchaField
from wtforms import StringField, PasswordField, BooleanField, SubmitField, SelectField, DecimalField
from wtforms import validators, ValidationError
from wtforms.fields.html5 import EmailField
class EditProfileForm(FlaskForm):
name = StringField('Лице за контакт:', [validators.DataRequired(), validators.Length(3, 60)])
address = StringField('Адрес:', [validators.DataRequired(), validators.Length(2, 50)])
city = StringField('Град:', [validators.DataRequired(), validators.Length(2,40)])
postcode = StringField('Пощенски Код:')
clist = []
for c in countries:
clist.append((c.alpha2, c.name))
country = SelectField('Държава:', choices=clist, default='BG')
phone = StringField('Телефон:')
org_responsible = StringField('Отговорно Лице:')
org_bulstat = StringField('БУЛСТАТ:')
twofactor = BooleanField('2-factor authentication')
submit = SubmitField('Обнови')
class EditProfileAdminForm(FlaskForm):
email = StringField('Електроннa поща (логин):', [validators.DataRequired(), validators.Length(1, 64), validators.Email()])
confirmed = BooleanField('Активиран')
role = SelectField('Роля', coerce=int)
name = StringField('Лице за контакт:', [validators.DataRequired(), validators.Length(3, 60)])
address = StringField('Адрес:', [validators.DataRequired(), validators.Length(2, 50)])
city = StringField('Град:', [validators.DataRequired(), validators.Length(2,40)])
postcode = DecimalField('Пощенски Код:')
clist = []
for c in countries:
clist.append((c.alpha2, c.name))
country = SelectField('Държава:', choices=clist)
phone = DecimalField('Телефон:', [validators.DataRequired()])
org_responsible = StringField('Отговорно Лице:')
org_bulstat = StringField('БУЛСТАТ:')
submit = SubmitField('Обнови')
def __init__(self, user, *args, **kwargs):
super(EditProfileAdminForm, self).__init__(*args, **kwargs)
self.role.choices = [(role.pid, role.name)
for role in Role.query.order_by(Role.name).all()]
self.user = user
def validate_email(self, field):
if field.data != self.user.email and User.query.filter_by(email=field.data).first():
raise ValidationError('Email-а е вече регистриран.')

51
app/settings/routes.py Normal file
View file

@ -0,0 +1,51 @@
from flask import render_template, redirect, request, url_for, flash, session, abort, current_app
from flask_login import login_required, login_user, logout_user, current_user
from sqlalchemy import desc
from . import settings
from .forms import EditProfileForm, EditProfileAdminForm, ChargeForm, PaymentForm
from ..email import send_email
from .. import db
from ..models import User, Order
#PROFILE
@settings.route('/profile', methods=['GET', 'POST'])
@login_required
def profile():
page = { 'title': 'Edit Profile' }
currentmail = current_user.email
ouruser = User.query.filter_by(email=currentmail).first()
db.session.commit()
#wallet = "%.2f" % round(ouruser.wallet, 3)
#print(wallet)
form = EditProfileForm()
if form.validate_on_submit():
current_user.name = form.name.data
current_user.address = form.address.data
current_user.city = form.city.data
current_user.postcode = form.postcode.data
current_user.country = form.country.data
current_user.phone = form.phone.data
current_user.org_responsible = form.org_responsible.data
current_user.org_bulstat = form.org_bulstat.data
current_user.twofactor = form.twofactor.data
db.session.add(current_user)
db.session.commit()
flash('Info Updated!')
form.twofactor.data = current_user.twofactor
form.name.data = current_user.name
form.address.data = current_user.address
form.city.data = current_user.city
form.postcode.data = current_user.postcode
form.country.data = current_user.country
form.phone.data = current_user.phone
form.org_responsible.data = current_user.org_responsible
form.org_bulstat.data = current_user.org_bulstat
return render_template('settings/profile.html', page=page, form=form)

View file

@ -47,7 +47,7 @@
<li><a href="{{ url_for('vmanager.dashboard') }}"><span class="glyphicon glyphicon-eye-open"></span> Dashboard</a></li> <li><a href="{{ url_for('vmanager.dashboard') }}"><span class="glyphicon glyphicon-eye-open"></span> Dashboard</a></li>
<li><a href="{{ url_for('uinvoice.documents') }}"><span class="glyphicon glyphicon-list-alt"></span> Orders</a></li> <li><a href="{{ url_for('uinvoice.documents') }}"><span class="glyphicon glyphicon-list-alt"></span> Orders</a></li>
<li role="separator" class="divider"></li> <li role="separator" class="divider"></li>
<li><a href="{{ url_for('uinvoice.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('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

@ -0,0 +1,10 @@
<div class="col-md-4">
<div class="panel panel-info">
<div class="panel-heading">{{ current_user.name }}</div>
<div class="panel-body">
<a href="https://en.gravatar.com/site/signup/"><img class="roundavatar" src="{{ current_user.gravatar(128) }}"></img></a><br />
2-Factor: {{ current_user.twofactor }}<br />
</div>
</div>
</div>

View file

@ -12,14 +12,14 @@
<div class="row"> <div class="row">
{% block sidebar %} {% block sidebar %}
{% include "uinvoice/_sidebar.html" %} {% include "settings/_sidebar.html" %}
{% endblock %} {% endblock %}
<div class="col-md-8"> <div class="col-md-8">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading">Данни на профила</div> <div class="panel-heading">Данни на профила</div>
<div class="panel-body"> <div class="panel-body">
<form method="POST" action="{{ url_for('uinvoice.profile') }}"> <form method="POST" action="{{ url_for('settings.profile') }}">
<p> <p>
{{ form.name.label }}<br />{{ form.name(size=42) }}<br /> {{ form.name.label }}<br />{{ form.name(size=42) }}<br />
{% for error in form.name.errors %} {% for error in form.name.errors %}

View file

@ -1,4 +1,3 @@
from iso3166 import countries
import string import string
import random import random
from ..models import User, Role from ..models import User, Role
@ -8,55 +7,6 @@ from wtforms import StringField, PasswordField, BooleanField, SubmitField, Selec
from wtforms import validators, ValidationError from wtforms import validators, ValidationError
from wtforms.fields.html5 import EmailField from wtforms.fields.html5 import EmailField
class EditProfileForm(FlaskForm):
name = StringField('Лице за контакт:', [validators.DataRequired(), validators.Length(3, 60)])
address = StringField('Адрес:', [validators.DataRequired(), validators.Length(2, 50)])
city = StringField('Град:', [validators.DataRequired(), validators.Length(2,40)])
postcode = StringField('Пощенски Код:')
clist = []
for c in countries:
clist.append((c.alpha2, c.name))
country = SelectField('Държава:', choices=clist, default='BG')
phone = StringField('Телефон:')
org_responsible = StringField('Отговорно Лице:')
org_bulstat = StringField('БУЛСТАТ:')
twofactor = BooleanField('2-factor authentication')
submit = SubmitField('Обнови')
class EditProfileAdminForm(FlaskForm):
email = StringField('Електроннa поща (логин):', [validators.DataRequired(), validators.Length(1, 64), validators.Email()])
confirmed = BooleanField('Активиран')
role = SelectField('Роля', coerce=int)
name = StringField('Лице за контакт:', [validators.DataRequired(), validators.Length(3, 60)])
address = StringField('Адрес:', [validators.DataRequired(), validators.Length(2, 50)])
city = StringField('Град:', [validators.DataRequired(), validators.Length(2,40)])
postcode = DecimalField('Пощенски Код:')
clist = []
for c in countries:
clist.append((c.alpha2, c.name))
country = SelectField('Държава:', choices=clist)
phone = DecimalField('Телефон:', [validators.DataRequired()])
org_responsible = StringField('Отговорно Лице:')
org_bulstat = StringField('БУЛСТАТ:')
submit = SubmitField('Обнови')
def __init__(self, user, *args, **kwargs):
super(EditProfileAdminForm, self).__init__(*args, **kwargs)
self.role.choices = [(role.pid, role.name)
for role in Role.query.order_by(Role.name).all()]
self.user = user
def validate_email(self, field):
if field.data != self.user.email and User.query.filter_by(email=field.data).first():
raise ValidationError('Email-а е вече регистриран.')
class ChargeForm(FlaskForm): class ChargeForm(FlaskForm):
invoice_amount = DecimalField('Стойност:', [validators.DataRequired(), validators.NumberRange(min=0, max=6)]) invoice_amount = DecimalField('Стойност:', [validators.DataRequired(), validators.NumberRange(min=0, max=6)])
submit = SubmitField('Зареди') submit = SubmitField('Зареди')

View file

@ -9,46 +9,6 @@ from ..email import send_email
from .. import db from .. import db
from ..models import User, Order from ..models import User, Order
#PROFILE
@uinvoice.route('/profile', methods=['GET', 'POST'])
@login_required
def profile():
page = { 'title': 'Edit Profile' }
currentmail = current_user.email
ouruser = User.query.filter_by(email=currentmail).first()
db.session.commit()
#wallet = "%.2f" % round(ouruser.wallet, 3)
#print(wallet)
form = EditProfileForm()
if form.validate_on_submit():
current_user.name = form.name.data
current_user.address = form.address.data
current_user.city = form.city.data
current_user.postcode = form.postcode.data
current_user.country = form.country.data
current_user.phone = form.phone.data
current_user.org_responsible = form.org_responsible.data
current_user.org_bulstat = form.org_bulstat.data
current_user.twofactor = form.twofactor.data
db.session.add(current_user)
db.session.commit()
flash('Info Updated!')
form.twofactor.data = current_user.twofactor
form.name.data = current_user.name
form.address.data = current_user.address
form.city.data = current_user.city
form.postcode.data = current_user.postcode
form.country.data = current_user.country
form.phone.data = current_user.phone
form.org_responsible.data = current_user.org_responsible
form.org_bulstat.data = current_user.org_bulstat
return render_template('uinvoice/profile.html', page=page, form=form)
#INVOICES #INVOICES
#@uinvoice.route('/charge', methods=['GET', 'POST']) #@uinvoice.route('/charge', methods=['GET', 'POST'])
#@login_required #@login_required