Switched to blueprints for the main app

master
Pierre Jaury 8 years ago
parent 1c1c8e9cf6
commit 40d4a22240

@ -10,7 +10,7 @@ import os
app = Flask(__name__)
default_config = {
'SQLALCHEMY_DATABASE_URI': 'sqlite:////tmp/freeposte.db',
'SQLALCHEMY_DATABASE_URI': 'sqlite:////data/freeposte.db',
'SQLALCHEMY_TRACK_MODIFICATIONS': False,
'SECRET_KEY': "changeMe",
'DEBUG': False
@ -20,24 +20,12 @@ default_config = {
for key, value in default_config.items():
app.config[key] = os.environ.get(key, value)
# Setup Bootstrap
# Setup components
Bootstrap(app)
# Create the database
db = SQLAlchemy(app)
# Import models once the database is ready
from freeposte import models
# Setup Flask-login
login_manager = flask_login.LoginManager()
login_manager.init_app(app)
login_manager.login_view = "login"
login_manager.user_loader(models.User.get_by_email)
@app.context_processor
def inject_user():
return dict(current_user=flask_login.current_user)
# Finally import view
from freeposte import views
# Finally setup the blueprint
from freeposte import admin
app.register_blueprint(admin.app, url_prefix='/admin')

@ -0,0 +1,28 @@
from flask import Blueprint
from flask.ext import login as flask_login
from freeposte import login_manager, db
app = Blueprint(
'admin', __name__,
template_folder='templates',
static_folder='static')
# Import models
from freeposte.admin import models
# Register the login components
login_manager.login_view = "admin.login"
login_manager.user_loader(models.User.get_by_email)
@app.context_processor
def inject_user():
return dict(current_user=flask_login.current_user)
# Import views
from freeposte.admin.views import \
administrators, \
base, \
aliases, \
users, \
domains

@ -1,9 +1,11 @@
from freeposte import db
from freeposte.admin import db
from sqlalchemy.ext import declarative
from passlib import context
from datetime import datetime
import re
# Many-to-many association table for domain administrators
admins = db.Table('admin',
@ -89,13 +91,16 @@ class User(Address):
is_active = True
is_anonymous = False
pw_context = context.CryptContext(["sha512_crypt", "sha256_crypt"])
pw_context = context.CryptContext(
["sha512_crypt", "sha256_crypt", "md5_crypt"]
)
def check_password(self, password):
return User.pw_context.verify(password, self.password)
reference = re.match('({[^}]+})?(.*)', self.password).group(2)
return User.pw_context.verify(password, reference)
def set_password(self, password):
self.password = User.pw_context.encrypt(password)
self.password = '{SHA512-CRYPT}' + User.pw_context.encrypt(password)
def get_managed_domains(self):
if self.global_admin:

@ -9,7 +9,7 @@ Alias list
{% endblock %}
{% block main_action %}
<a class="btn btn-primary" href="{{ url_for('alias_create', domain_name=domain.name) }}">Add alias</a>
<a class="btn btn-primary" href="{{ url_for('.alias_create', domain_name=domain.name) }}">Add alias</a>
{% endblock %}
{% block box %}
@ -26,8 +26,8 @@ Alias list
{% for alias in domain.aliases %}
<tr>
<td>
<a href="{{ url_for('alias_edit', alias=alias.get_id()) }}" title="Edit"><i class="fa fa-pencil"></i></a>&nbsp;
<a href="{{ url_for('alias_delete', alias=alias.get_id()) }}" title="Delete"><i class="fa fa-trash"></i></a>
<a href="{{ url_for('.alias_edit', alias=alias.get_id()) }}" title="Edit"><i class="fa fa-pencil"></i></a>&nbsp;
<a href="{{ url_for('.alias_delete', alias=alias.get_id()) }}" title="Delete"><i class="fa fa-trash"></i></a>
</td>
<td>{{ alias }}</td>
<td>{{ alias.destination or '-' }}</td>

@ -5,8 +5,8 @@
{% block styles %}
{{super()}}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<link rel="stylesheet" href="/static/adminlte/css/AdminLTE.min.css">
<link rel="stylesheet" href="/static/adminlte/css/skin-blue.min.css">
<link rel="stylesheet" href="{{ url_for('.static', filename='adminlte/css/AdminLTE.min.css') }}">
<link rel="stylesheet" href="{{ url_for('.static', filename='adminlte/css/skin-blue.min.css') }}">
{% endblock %}
{% block body_attribs %}
@ -68,7 +68,7 @@ class="hold-transition skin-blue sidebar-mini"
{% block scripts %}
{{super()}}
<script src="/static/adminlte/js/app.min.js"></script>
<script src="{{ url_for('.static', filename='adminlte/js/app.min.js') }}"></script>
{% endblock %}
</div>
{% endblock %}

@ -6,7 +6,7 @@ Domain list
{% block main_action %}
{% if current_user.global_admin %}
<a class="btn btn-primary" href="{{ url_for('domain_create') }}">New domain</a>
<a class="btn btn-primary" href="{{ url_for('.domain_create') }}">New domain</a>
{% endif %}
{% endblock %}
@ -25,11 +25,11 @@ Domain list
{% for domain in current_user.get_managed_domains() %}
<tr>
<td>
<a href="{{ url_for('user_list', domain_name=domain.name) }}" title="Users"><i class="fa fa-envelope-o"></i></a>&nbsp;
<a href="{{ url_for('alias_list', domain_name=domain.name) }}" title="Aliases"><i class="fa fa-at"></i></a>&nbsp;
<a href="{{ url_for('domain_admins', domain_name=domain.name) }}" title="Administrators"><i class="fa fa-user"></i></a>&nbsp;
<a href="{{ url_for('domain_delete', domain_name=domain.name) }}" title="Delete"><i class="fa fa-trash"></i></a>&nbsp;
<a href="{{ url_for('domain_edit', domain_name=domain.name) }}" title="Edit"><i class="fa fa-pencil"></i></a>
<a href="{{ url_for('.user_list', domain_name=domain.name) }}" title="Users"><i class="fa fa-envelope-o"></i></a>&nbsp;
<a href="{{ url_for('.alias_list', domain_name=domain.name) }}" title="Aliases"><i class="fa fa-at"></i></a>&nbsp;
<a href="{{ url_for('.domain_admins', domain_name=domain.name) }}" title="Administrators"><i class="fa fa-user"></i></a>&nbsp;
<a href="{{ url_for('.domain_delete', domain_name=domain.name) }}" title="Delete"><i class="fa fa-trash"></i></a>&nbsp;
<a href="{{ url_for('.domain_edit', domain_name=domain.name) }}" title="Edit"><i class="fa fa-pencil"></i></a>
</td>
<td>{{ domain.name }}</td>
<td>{{ domain.users | count }} / {{ domain.max_users or '∞' }}</td>

@ -4,51 +4,51 @@
<ul class="sidebar-menu">
<li class="header">My account</li>
<li>
<a href="{{ url_for('user_settings') }}">
<a href="{{ url_for('.user_settings') }}">
<i class="fa fa-wrench"></i> <span>Settings</span>
</a>
</li>
<li>
<a href="{{ url_for('user_password') }}">
<a href="{{ url_for('.user_password') }}">
<i class="fa fa-lock"></i> <span>Update password</span>
</a>
</li>
<li>
<a href="{{ url_for('user_forward') }}">
<a href="{{ url_for('.user_forward') }}">
<i class="fa fa-share"></i> <span>Auto-forward</span>
</a>
</li>
<li>
<a href="{{ url_for('user_reply') }}">
<a href="{{ url_for('.user_reply') }}">
<i class="fa fa-plane"></i> <span>Auto-reply</span>
</a>
</li>
<li>
<a href="{{ url_for('user_fetchmail') }}">
<a href="{{ url_for('.user_fetchmail') }}">
<i class="fa fa-download"></i> <span>Fetched accounts</span>
</a>
</li>
<li>
<a href="{{ url_for('logout') }}">
<a href="{{ url_for('.logout') }}">
<i class="fa fa-sign-out"></i> <span>Sign out</span>
</a>
</li>
<li class="header">Administration</li>
{% if current_user.global_admin %}
<li>
<a href="{{ url_for('status') }}">
<a href="{{ url_for('.status') }}">
<i class="fa fa-dashboard"></i> <span>Services status</span>
</a>
</li>
<li>
<a href="{{ url_for('admins') }}">
<a href="{{ url_for('.admins') }}">
<i class="fa fa-user"></i> <span>Adminitrators</span>
</a>
</li>
{% endif %}
{% if current_user.admin_of %}
{% if current_user.admin_of or current_user.global_admin %}
<li>
<a href="{{ url_for('domain_list') }}">
<a href="{{ url_for('.domain_list') }}">
<i class="fa fa-envelope"></i> <span>Mail domains</span>
</a>
</li>

@ -0,0 +1,52 @@
{% extends "base.html" %}
{% block title %}
User list
{% endblock %}
{% block subtitle %}
{{ domain.name }}
{% endblock %}
{% block main_action %}
<a class="btn btn-primary" href="{{ url_for('.user_create', domain_name=domain.name) }}">Add user</a>
{% endblock %}
{% block box %}
<table class="table table-bordered">
<tbody>
<tr>
<th>Actions</th>
<th>Address</th>
<th>Name</th>
<th>Forward</th>
<th>Reply</th>
<th>Quota</th>
<th>Comment</th>
<th>Created</th>
<th>Last edit</th>
</tr>
{% for user in domain.users %}
<tr>
<td>
<a href="{{ url_for('.user_settings', user_email=user.get_id()) }}" title="Settings"><i class="fa fa-wrench"></i></a>&nbsp;
<a href="{{ url_for('.user_password', user_email=user.get_id()) }}" title="Update password"><i class="fa fa-lock"></i></a>&nbsp;
<a href="{{ url_for('.user_forward', user_email=user.get_id()) }}" title="Auto-forward"><i class="fa fa-share"></i></a>&nbsp;
<a href="{{ url_for('.user_reply', user_email=user.get_id()) }}" title="Auto-reply"><i class="fa fa-plane"></i></a>&nbsp;
<a href="{{ url_for('.user_fetchmail', user_email=user.get_id()) }}" title="Fetched accounts"><i class="fa fa-download"></i></a>&nbsp;
<a href="{{ url_for('.user_edit', user_email=user.get_id()) }}" title="Edit"><i class="fa fa-pencil"></i></a>&nbsp;
<a href="{{ url_for('.user_delete', user_email=user.get_id()) }}" title="Delete"><i class="fa fa-trash"></i></a>
</td>
<td>{{ user }}</td>
<td>{{ user.displayed_name }}</td>
<td>{% if user.forward %}<span class="label label-info">enabled</span>{% endif %}</td>
<td>{% if user.reply_subject %}<span class="label label-info">enabled</span>{% endif %}</td>
<td>{{ user.quota_bytes | filesizeformat }}</td>
<td>{{ user.comment or '' }}</td>
<td>{{ user.created_at }}</td>
<td>{{ user.updated_at or '' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

@ -1,4 +1,4 @@
from freeposte import models
from freeposte.admin import models
from flask.ext import login as flask_login
import flask

@ -1,4 +1,4 @@
from freeposte import app, db, models, forms, utils
from freeposte.admin import app, db, models, forms, utils
from flask.ext import login as flask_login
import os

@ -1,4 +1,4 @@
from freeposte import app, db, models, forms, utils
from freeposte.admin import app, db, models, forms, utils
from flask.ext import login as flask_login
import os
@ -18,7 +18,8 @@ def alias_create(domain_name):
domain = utils.get_domain_admin(domain_name)
if domain.max_aliases and len(domain.aliases) >= domain.max_aliases:
flask.flash('Too many aliases for domain %s' % domain, 'error')
return flask.redirect(flask.url_for('alias_list', domain_name=domain.name))
return flask.redirect(
flask.url_for('.alias_list', domain_name=domain.name))
form = forms.AliasCreateForm()
if form.validate_on_submit():
for address in domain.users + domain.aliases:
@ -33,7 +34,7 @@ def alias_create(domain_name):
db.session.commit()
flask.flash('Alias %s created' % alias)
return flask.redirect(
flask.url_for('alias_list', domain_name=domain.name))
flask.url_for('.alias_list', domain_name=domain.name))
return flask.render_template('alias/create.html',
domain=domain, form=form)
@ -50,7 +51,7 @@ def alias_edit(alias):
db.session.commit()
flask.flash('Alias %s updated' % alias)
return flask.redirect(
flask.url_for('alias_list', domain_name=alias.domain.name))
flask.url_for('.alias_list', domain_name=alias.domain.name))
return flask.render_template('alias/edit.html', form=form, alias=alias)
@ -61,4 +62,5 @@ def alias_delete(alias):
db.session.delete(alias)
db.session.commit()
flask.flash('Alias %s deleted' % alias)
return flask.redirect(flask.url_for('alias_list', domain_name=alias.domain.name))
return flask.redirect(
flask.url_for('.alias_list', domain_name=alias.domain.name))

@ -1,4 +1,4 @@
from freeposte import app, db, models, forms
from freeposte.admin import app, db, models, forms
from flask.ext import login as flask_login
import os
@ -8,7 +8,7 @@ import flask
@app.route('/', methods=["GET"])
@flask_login.login_required
def index():
return flask.redirect(flask.url_for('user_settings'))
return flask.redirect(flask.url_for('.user_settings'))
@app.route('/login', methods=['GET', 'POST'])
@ -18,7 +18,7 @@ def login():
user = models.User.login(form.email.data, form.pw.data)
if user:
flask_login.login_user(user)
return flask.redirect(flask.url_for('index'))
return flask.redirect(flask.url_for('.index'))
else:
flask.flash('Wrong e-mail address or password', 'error')
return flask.render_template('login.html', form=form)
@ -28,4 +28,4 @@ def login():
@flask_login.login_required
def logout():
flask_login.logout_user()
return flask.redirect(flask.url_for('index'))
return flask.redirect(flask.url_for('.index'))

@ -1,4 +1,4 @@
from freeposte import app, db, models, forms, utils
from freeposte.admin import app, db, models, forms, utils
from flask.ext import login as flask_login
import os
@ -25,7 +25,7 @@ def domain_create():
db.session.add(domain)
db.session.commit()
flask.flash('Domain %s created' % domain)
return flask.redirect(flask.url_for('domain_list'))
return flask.redirect(flask.url_for('.domain_list'))
return flask.render_template('domain/create.html', form=form)
@ -42,7 +42,7 @@ def domain_edit(domain_name):
db.session.add(domain)
db.session.commit()
flask.flash('Domain %s saved' % domain)
return flask.redirect(flask.url_for('domain_list'))
return flask.redirect(flask.url_for('.domain_list'))
return flask.render_template('domain/edit.html', form=form,
domain=domain)
@ -55,7 +55,7 @@ def domain_delete(domain_name):
db.session.delete(domain)
db.session.commit()
flask.flash('Domain %s deleted' % domain)
return flask.redirect(flask.url_for('domain_list'))
return flask.redirect(flask.url_for('.domain_list'))
@app.route('/domain/admins/<domain_name>', methods=['GET'])

@ -1,4 +1,4 @@
from freeposte import app, db, models, forms, utils
from freeposte.admin import app, db, models, forms, utils
from flask.ext import login as flask_login
import os
@ -16,9 +16,10 @@ def user_list(domain_name):
@flask_login.login_required
def user_create(domain_name):
domain = utils.get_domain_admin(domain_name)
if domain.mex_users and len(domain.users) >= domain.max_users:
if domain.max_users and len(domain.users) >= domain.max_users:
flask.flash('Too many users for domain %s' % domain, 'error')
return flask.redirect(flask.url_for('user_list', domain_name=domain.name))
return flask.redirect(
flask.url_for('.user_list', domain_name=domain.name))
form = forms.UserCreateForm()
if form.validate_on_submit():
for address in domain.users + domain.aliases:
@ -33,7 +34,7 @@ def user_create(domain_name):
db.session.commit()
flask.flash('User %s created' % user)
return flask.redirect(
flask.url_for('user_list', domain_name=domain.name))
flask.url_for('.user_list', domain_name=domain.name))
return flask.render_template('user/create.html',
domain=domain, form=form)
@ -50,7 +51,7 @@ def user_edit(user_email):
db.session.commit()
flask.flash('User %s updated' % user)
return flask.redirect(
flask.url_for('user_list', domain_name=user.domain.name))
flask.url_for('.user_list', domain_name=user.domain.name))
return flask.render_template('user/edit.html', form=form, user=user)
@ -61,7 +62,8 @@ def user_delete(user_email):
db.session.delete(user)
db.session.commit()
flask.flash('User %s deleted' % user)
return flask.redirect(flask.url_for('user_list', domain_name=user.domain.name))
return flask.redirect(
flask.url_for('.user_list', domain_name=user.domain.name))
@app.route('/user/settings', methods=['GET', 'POST'], defaults={'user_email': None})
@ -79,7 +81,7 @@ def user_settings(user_email):
flask.flash('Settings updated for %s' % user)
if user_email:
return flask.redirect(
flask.url_for('user_list', domain_name=user.domain.name))
flask.url_for('.user_list', domain_name=user.domain.name))
return flask.render_template('user/settings.html', form=form, user=user)
@ -98,8 +100,8 @@ def user_password(user_email):
db.session.commit()
flask.flash('Password updated for %s' % user)
if user_email:
return flask.redirect(
flask.url_for('user_list', domain_name=user.domain.name))
return flask.redirect(flask.url_for('.user_list',
domain_name=user.domain.name))
return flask.render_template('user/password.html', form=form, user=user)
@ -116,7 +118,7 @@ def user_forward(user_email):
flask.flash('Forward destination updated for %s' % user)
if user_email:
return flask.redirect(
flask.url_for('user_list', domain_name=user.domain.name))
flask.url_for('.user_list', domain_name=user.domain.name))
return flask.render_template('user/forward.html', form=form, user=user)
@ -134,7 +136,7 @@ def user_reply(user_email):
flask.flash('Auto-reply message updated for %s' % user)
if user_email:
return flask.redirect(
flask.url_for('user_list', domain_name=user.domain.name))
flask.url_for('.user_list', domain_name=user.domain.name))
return flask.render_template('user/reply.html', form=form, user=user)

@ -1,52 +0,0 @@
{% extends "base.html" %}
{% block title %}
User list
{% endblock %}
{% block subtitle %}
{{ domain.name }}
{% endblock %}
{% block main_action %}
<a class="btn btn-primary" href="{{ url_for('user_create', domain_name=domain.name) }}">Add user</a>
{% endblock %}
{% block box %}
<table class="table table-bordered">
<tbody>
<tr>
<th>Actions</th>
<th>Address</th>
<th>Name</th>
<th>Forward</th>
<th>Reply</th>
<th>Quota</th>
<th>Comment</th>
<th>Created</th>
<th>Last edit</th>
</tr>
{% for user in domain.users %}
<tr>
<td>
<a href="{{ url_for('user_settings', user_email=user.get_id()) }}" title="Settings"><i class="fa fa-wrench"></i></a>&nbsp;
<a href="{{ url_for('user_password', user_email=user.get_id()) }}" title="Update password"><i class="fa fa-lock"></i></a>&nbsp;
<a href="{{ url_for('user_forward', user_email=user.get_id()) }}" title="Auto-forward"><i class="fa fa-share"></i></a>&nbsp;
<a href="{{ url_for('user_reply', user_email=user.get_id()) }}" title="Auto-reply"><i class="fa fa-plane"></i></a>&nbsp;
<a href="{{ url_for('user_fetchmail', user_email=user.get_id()) }}" title="Fetched accounts"><i class="fa fa-download"></i></a>&nbsp;
<a href="{{ url_for('user_edit', user_email=user.get_id()) }}" title="Edit"><i class="fa fa-pencil"></i></a>&nbsp;
<a href="{{ url_for('user_delete', user_email=user.get_id()) }}" title="Delete"><i class="fa fa-trash"></i></a>
</td>
<td>{{ user }}</td>
<td>{{ user.displayed_name }}</td>
<td>{% if user.forward %}<span class="label label-info">enabled</span>{% endif %}</td>
<td>{% if user.reply_subject %}<span class="label label-info">enabled</span>{% endif %}</td>
<td>{{ user.quota_bytes | filesizeformat }}</td>
<td>{{ user.comment or '' }}</td>
<td>{{ user.created_at }}</td>
<td>{{ user.updated_at or '' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

@ -1 +0,0 @@
from freeposte.views import base, admin, domains, users, aliases
Loading…
Cancel
Save