First batch of refactoring, using the app factory pattern
							parent
							
								
									7c82be904f
								
							
						
					
					
						commit
						fc24426291
					
				| @ -1,132 +1,58 @@ | ||||
| import flask | ||||
| import flask_sqlalchemy | ||||
| import flask_bootstrap | ||||
| import flask_login | ||||
| import flask_script | ||||
| import flask_migrate | ||||
| import flask_babel | ||||
| import flask_limiter | ||||
| 
 | ||||
| import os | ||||
| import docker | ||||
| import socket | ||||
| import uuid | ||||
| 
 | ||||
| from werkzeug.contrib import fixers | ||||
| from mailu import utils, debug, db | ||||
| 
 | ||||
| # Create application | ||||
| app = flask.Flask(__name__) | ||||
| 
 | ||||
| default_config = { | ||||
|     # Specific to the admin UI | ||||
|     'SQLALCHEMY_DATABASE_URI': 'sqlite:////data/main.db', | ||||
|     'SQLALCHEMY_TRACK_MODIFICATIONS': False, | ||||
|     'DOCKER_SOCKET': 'unix:///var/run/docker.sock', | ||||
|     'BABEL_DEFAULT_LOCALE': 'en', | ||||
|     'BABEL_DEFAULT_TIMEZONE': 'UTC', | ||||
|     'BOOTSTRAP_SERVE_LOCAL': True, | ||||
|     'RATELIMIT_STORAGE_URL': 'redis://redis/2', | ||||
|     'QUOTA_STORAGE_URL': 'redis://redis/1', | ||||
|     'DEBUG': False, | ||||
|     'DOMAIN_REGISTRATION': False, | ||||
|     # Statistics management | ||||
|     'INSTANCE_ID_PATH': '/data/instance', | ||||
|     'STATS_ENDPOINT': '0.{}.stats.mailu.io', | ||||
|     # Common configuration variables | ||||
|     'SECRET_KEY': 'changeMe', | ||||
|     'DOMAIN': 'mailu.io', | ||||
|     'HOSTNAMES': 'mail.mailu.io,alternative.mailu.io,yetanother.mailu.io', | ||||
|     'POSTMASTER': 'postmaster', | ||||
|     'TLS_FLAVOR': 'cert', | ||||
|     'AUTH_RATELIMIT': '10/minute;1000/hour', | ||||
|     'DISABLE_STATISTICS': 'False', | ||||
|     # Mail settings | ||||
|     'DMARC_RUA': None, | ||||
|     'DMARC_RUF': None, | ||||
|     'WELCOME': 'False', | ||||
|     'WELCOME_SUBJECT': 'Dummy welcome topic', | ||||
|     'WELCOME_BODY': 'Dummy welcome body', | ||||
|     'DKIM_SELECTOR': 'dkim', | ||||
|     'DKIM_PATH': '/dkim/{domain}.{selector}.key', | ||||
|     'DEFAULT_QUOTA': 1000000000, | ||||
|     # Web settings | ||||
|     'SITENAME': 'Mailu', | ||||
|     'WEBSITE': 'https://mailu.io', | ||||
|     'WEB_ADMIN': '/admin', | ||||
|     'WEB_WEBMAIL': '/webmail', | ||||
|     'RECAPTCHA_PUBLIC_KEY': '', | ||||
|     'RECAPTCHA_PRIVATE_KEY': '', | ||||
|     # Advanced settings | ||||
|     'PASSWORD_SCHEME': 'BLF-CRYPT', | ||||
|     # Host settings | ||||
|     'HOST_IMAP': 'imap', | ||||
|     'HOST_POP3': 'imap', | ||||
|     'HOST_SMTP': 'smtp', | ||||
|     'HOST_AUTHSMTP': os.environ.get('HOST_SMTP', 'smtp'), | ||||
| } | ||||
| def create_app_from_config(config): | ||||
|     """ Create a new application based on the given configuration | ||||
|     """ | ||||
|     app = flask.Flask(__name__) | ||||
|     app.config = config | ||||
| 
 | ||||
| # Load configuration from the environment if available | ||||
| for key, value in default_config.items(): | ||||
|     app.config[key] = os.environ.get(key, value) | ||||
|     # Bootstrap is used for basic JS and CSS loading | ||||
|     # TODO: remove this and use statically generated assets instead | ||||
|     app.bootstrap = flask_bootstrap.Bootstrap(app) | ||||
| 
 | ||||
| # Base application | ||||
| flask_bootstrap.Bootstrap(app) | ||||
| db = flask_sqlalchemy.SQLAlchemy(app) | ||||
| migrate = flask_migrate.Migrate(app, db) | ||||
| limiter = flask_limiter.Limiter(app, key_func=lambda: current_user.username) | ||||
|     # Initialize application extensions | ||||
|     models.db.init_app(app) | ||||
|     utils.limiter.init_app(app) | ||||
|     utils.babel.init_app(app) | ||||
|     utils.login.init_app(app) | ||||
|     utils.proxy.init_app(app) | ||||
|     manage.migrate.init_app(app) | ||||
|     manage.manager.init_app(app) | ||||
| 
 | ||||
| # Debugging toolbar | ||||
| if app.config.get("DEBUG"): | ||||
|     import flask_debugtoolbar | ||||
|     toolbar = flask_debugtoolbar.DebugToolbarExtension(app) | ||||
|     # Initialize debugging tools | ||||
|     if app.config.get("app.debug"): | ||||
|         debug.toolbar.init_app(app) | ||||
|         debug.profiler.init_app(app) | ||||
| 
 | ||||
| # Manager commnad | ||||
| manager = flask_script.Manager(app) | ||||
| manager.add_command('db', flask_migrate.MigrateCommand) | ||||
|     # Inject the default variables in the Jinja parser | ||||
|     @app.context_processor | ||||
|     def inject_defaults(): | ||||
|         signup_domains = models.Domain.query.filter_by(signup_enabled=True).all() | ||||
|         return dict( | ||||
|             current_user=utils.login.current_user, | ||||
|             signup_domains=signup_domains, | ||||
|             config=app.config | ||||
|         ) | ||||
| 
 | ||||
| # Babel configuration | ||||
| babel = flask_babel.Babel(app) | ||||
| translations = list(map(str, babel.list_translations())) | ||||
|     # Import views | ||||
|     from mailu import ui, internal | ||||
|     app.register_blueprint(ui.ui, url_prefix='/ui') | ||||
|     app.register_blueprint(internal.internal, url_prefix='/internal') | ||||
| 
 | ||||
| @babel.localeselector | ||||
| def get_locale(): | ||||
|     return flask.request.accept_languages.best_match(translations) | ||||
|     return app | ||||
| 
 | ||||
| # Login configuration | ||||
| login_manager = flask_login.LoginManager() | ||||
| login_manager.init_app(app) | ||||
| login_manager.login_view = "ui.login" | ||||
| 
 | ||||
| @login_manager.unauthorized_handler | ||||
| def handle_needs_login(): | ||||
|     return flask.redirect( | ||||
|         flask.url_for('ui.login', next=flask.request.endpoint) | ||||
|     ) | ||||
| 
 | ||||
| @app.context_processor | ||||
| def inject_defaults(): | ||||
|     signup_domains = models.Domain.query.filter_by(signup_enabled=True).all() | ||||
|     return dict( | ||||
|         current_user=flask_login.current_user, | ||||
|         signup_domains=signup_domains, | ||||
|         config=app.config | ||||
|     ) | ||||
| 
 | ||||
| # Import views | ||||
| from mailu import ui, internal | ||||
| app.register_blueprint(ui.ui, url_prefix='/ui') | ||||
| app.register_blueprint(internal.internal, url_prefix='/internal') | ||||
| 
 | ||||
| # Create the prefix middleware | ||||
| class PrefixMiddleware(object): | ||||
| 
 | ||||
|     def __init__(self, app): | ||||
|         self.app = app | ||||
| 
 | ||||
|     def __call__(self, environ, start_response): | ||||
|         prefix = environ.get('HTTP_X_FORWARDED_PREFIX', '') | ||||
|         if prefix: | ||||
|             environ['SCRIPT_NAME'] = prefix | ||||
|         return self.app(environ, start_response) | ||||
| 
 | ||||
| app.wsgi_app = PrefixMiddleware(fixers.ProxyFix(app.wsgi_app)) | ||||
| def create_app(): | ||||
|     """ Create a new application based on the config module  | ||||
|     """ | ||||
|     config = configuration.ConfigManager() | ||||
|     return create_app_from_config(config) | ||||
|  | ||||
| @ -0,0 +1,70 @@ | ||||
| import os | ||||
| 
 | ||||
| 
 | ||||
| DEFAULT_CONFIG = { | ||||
|     # Specific to the admin UI | ||||
|     'SQLALCHEMY_DATABASE_URI': 'sqlite:////data/main.db', | ||||
|     'SQLALCHEMY_TRACK_MODIFICATIONS': False, | ||||
|     'DOCKER_SOCKET': 'unix:///var/run/docker.sock', | ||||
|     'BABEL_DEFAULT_LOCALE': 'en', | ||||
|     'BABEL_DEFAULT_TIMEZONE': 'UTC', | ||||
|     'BOOTSTRAP_SERVE_LOCAL': True, | ||||
|     'RATELIMIT_STORAGE_URL': 'redis://redis/2', | ||||
|     'QUOTA_STORAGE_URL': 'redis://redis/1', | ||||
|     'DEBUG': False, | ||||
|     'DOMAIN_REGISTRATION': False, | ||||
|     # Statistics management | ||||
|     'INSTANCE_ID_PATH': '/data/instance', | ||||
|     'STATS_ENDPOINT': '0.{}.stats.mailu.io', | ||||
|     # Common configuration variables | ||||
|     'SECRET_KEY': 'changeMe', | ||||
|     'DOMAIN': 'mailu.io', | ||||
|     'HOSTNAMES': 'mail.mailu.io,alternative.mailu.io,yetanother.mailu.io', | ||||
|     'POSTMASTER': 'postmaster', | ||||
|     'TLS_FLAVOR': 'cert', | ||||
|     'AUTH_RATELIMIT': '10/minute;1000/hour', | ||||
|     'DISABLE_STATISTICS': 'False', | ||||
|     # Mail settings | ||||
|     'DMARC_RUA': None, | ||||
|     'DMARC_RUF': None, | ||||
|     'WELCOME': 'False', | ||||
|     'WELCOME_SUBJECT': 'Dummy welcome topic', | ||||
|     'WELCOME_BODY': 'Dummy welcome body', | ||||
|     'DKIM_SELECTOR': 'dkim', | ||||
|     'DKIM_PATH': '/dkim/{domain}.{selector}.key', | ||||
|     'DEFAULT_QUOTA': 1000000000, | ||||
|     # Web settings | ||||
|     'SITENAME': 'Mailu', | ||||
|     'WEBSITE': 'https://mailu.io', | ||||
|     'WEB_ADMIN': '/admin', | ||||
|     'WEB_WEBMAIL': '/webmail', | ||||
|     'RECAPTCHA_PUBLIC_KEY': '', | ||||
|     'RECAPTCHA_PRIVATE_KEY': '', | ||||
|     # Advanced settings | ||||
|     'PASSWORD_SCHEME': 'BLF-CRYPT', | ||||
|     # Host settings | ||||
|     'HOST_IMAP': 'imap', | ||||
|     'HOST_POP3': 'imap', | ||||
|     'HOST_SMTP': 'smtp', | ||||
|     'HOST_WEBMAIL': 'webmail', | ||||
|     'HOST_FRONT': 'front', | ||||
|     'HOST_AUTHSMTP': os.environ.get('HOST_SMTP', 'smtp'), | ||||
|     'POD_ADDRESS_RANGE': None | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| class ConfigManager(object): | ||||
|     """ Naive configuration manager that uses environment only | ||||
|     """ | ||||
| 
 | ||||
|     def __init__(self): | ||||
|         self.config = { | ||||
|             os.environ.get(key, value) | ||||
|             for key, value in DEFAULT_CONFIG.items() | ||||
|         } | ||||
| 
 | ||||
|     def get(self, *args): | ||||
|         return self.config.get(*args) | ||||
| 
 | ||||
|     def __getitem__(self, key): | ||||
|         return self.get(key) | ||||
| @ -0,0 +1,17 @@ | ||||
| import flask_debugtoolbar | ||||
| 
 | ||||
| from werkzeug.contrib import profiler as werkzeug_profiler | ||||
| 
 | ||||
| 
 | ||||
| # Debugging toolbar | ||||
| toolbar = flask_debugtoolbar.DebugToolbarExtension() | ||||
| 
 | ||||
| 
 | ||||
| # Profiler | ||||
| class Profiler(object): | ||||
|     def init_app(self): | ||||
|         app.wsgi_app = werkzeug_profiler.ProfilerMiddleware( | ||||
|             app.wsgi_app, restrictions=[30] | ||||
|         ) | ||||
| 
 | ||||
| profiler = Profiler() | ||||
| @ -0,0 +1,305 @@ | ||||
| from mailu import models | ||||
| 
 | ||||
| from flask import current_app as app | ||||
| 
 | ||||
| import flask | ||||
| import os | ||||
| import socket | ||||
| import uuid | ||||
| 
 | ||||
| 
 | ||||
| manager = flask_script.Manager() | ||||
| db = models.db | ||||
| 
 | ||||
| 
 | ||||
| @manager.command | ||||
| def advertise(): | ||||
|     """ Advertise this server against statistic services. | ||||
|     """ | ||||
|     if os.path.isfile(app.config["INSTANCE_ID_PATH"]): | ||||
|         with open(app.config["INSTANCE_ID_PATH"], "r") as handle: | ||||
|             instance_id = handle.read() | ||||
|     else: | ||||
|         instance_id = str(uuid.uuid4()) | ||||
|         with open(app.config["INSTANCE_ID_PATH"], "w") as handle: | ||||
|             handle.write(instance_id) | ||||
|     if app.config["DISABLE_STATISTICS"].lower() != "true": | ||||
|         try: | ||||
|             socket.gethostbyname(app.config["STATS_ENDPOINT"].format(instance_id)) | ||||
|         except: | ||||
|             pass | ||||
| 
 | ||||
| 
 | ||||
| @manager.command | ||||
| def admin(localpart, domain_name, password): | ||||
|     """ Create an admin user | ||||
|     """ | ||||
|     domain = models.Domain.query.get(domain_name) | ||||
|     if not domain: | ||||
|         domain = models.Domain(name=domain_name) | ||||
|         db.session.add(domain) | ||||
|     user = models.User( | ||||
|         localpart=localpart, | ||||
|         domain=domain, | ||||
|         global_admin=True | ||||
|     ) | ||||
|     user.set_password(password) | ||||
|     db.session.add(user) | ||||
|     db.session.commit() | ||||
| 
 | ||||
| 
 | ||||
| @manager.command | ||||
| def user(localpart, domain_name, password, | ||||
|          hash_scheme=app.config['PASSWORD_SCHEME']): | ||||
|     """ Create a user | ||||
|     """ | ||||
|     domain = models.Domain.query.get(domain_name) | ||||
|     if not domain: | ||||
|         domain = models.Domain(name=domain_name) | ||||
|         db.session.add(domain) | ||||
|     user = models.User( | ||||
|         localpart=localpart, | ||||
|         domain=domain, | ||||
|         global_admin=False | ||||
|     ) | ||||
|     user.set_password(password, hash_scheme=hash_scheme) | ||||
|     db.session.add(user) | ||||
|     db.session.commit() | ||||
| 
 | ||||
| 
 | ||||
| @manager.option('-n', '--domain_name', dest='domain_name') | ||||
| @manager.option('-u', '--max_users', dest='max_users') | ||||
| @manager.option('-a', '--max_aliases', dest='max_aliases') | ||||
| @manager.option('-q', '--max_quota_bytes', dest='max_quota_bytes') | ||||
| def domain(domain_name, max_users=0, max_aliases=0, max_quota_bytes=0): | ||||
|     domain = models.Domain.query.get(domain_name) | ||||
|     if not domain: | ||||
|         domain = models.Domain(name=domain_name) | ||||
|         db.session.add(domain) | ||||
|         db.session.commit() | ||||
| 
 | ||||
| 
 | ||||
| @manager.command | ||||
| def user_import(localpart, domain_name, password_hash, | ||||
|                 hash_scheme=app.config['PASSWORD_SCHEME']): | ||||
|     """ Import a user along with password hash. Available hashes: | ||||
|                    'SHA512-CRYPT' | ||||
|                    'SHA256-CRYPT' | ||||
|                    'MD5-CRYPT' | ||||
|                    'CRYPT' | ||||
|     """ | ||||
|     domain = models.Domain.query.get(domain_name) | ||||
|     if not domain: | ||||
|         domain = models.Domain(name=domain_name) | ||||
|         db.session.add(domain) | ||||
|     user = models.User( | ||||
|         localpart=localpart, | ||||
|         domain=domain, | ||||
|         global_admin=False | ||||
|     ) | ||||
|     user.set_password(password_hash, hash_scheme=hash_scheme, raw=True) | ||||
|     db.session.add(user) | ||||
|     db.session.commit() | ||||
| 
 | ||||
| 
 | ||||
| @manager.command | ||||
| def config_update(verbose=False, delete_objects=False): | ||||
|     """sync configuration with data from YAML-formatted stdin""" | ||||
|     import yaml | ||||
|     import sys | ||||
|     new_config = yaml.load(sys.stdin) | ||||
|     # print new_config | ||||
|     domains = new_config.get('domains', []) | ||||
|     tracked_domains = set() | ||||
|     for domain_config in domains: | ||||
|         if verbose: | ||||
|             print(str(domain_config)) | ||||
|         domain_name = domain_config['name'] | ||||
|         max_users = domain_config.get('max_users', 0) | ||||
|         max_aliases = domain_config.get('max_aliases', 0) | ||||
|         max_quota_bytes = domain_config.get('max_quota_bytes', 0) | ||||
|         tracked_domains.add(domain_name) | ||||
|         domain = models.Domain.query.get(domain_name) | ||||
|         if not domain: | ||||
|             domain = models.Domain(name=domain_name, | ||||
|                                    max_users=max_users, | ||||
|                                    max_aliases=max_aliases, | ||||
|                                    max_quota_bytes=max_quota_bytes) | ||||
|             db.session.add(domain) | ||||
|             print("Added " + str(domain_config)) | ||||
|         else: | ||||
|             domain.max_users = max_users | ||||
|             domain.max_aliases = max_aliases | ||||
|             domain.max_quota_bytes = max_quota_bytes | ||||
|             db.session.add(domain) | ||||
|             print("Updated " + str(domain_config)) | ||||
| 
 | ||||
|     users = new_config.get('users', []) | ||||
|     tracked_users = set() | ||||
|     user_optional_params = ('comment', 'quota_bytes', 'global_admin', | ||||
|                             'enable_imap', 'enable_pop', 'forward_enabled', | ||||
|                             'forward_destination', 'reply_enabled', | ||||
|                             'reply_subject', 'reply_body', 'displayed_name', | ||||
|                             'spam_enabled', 'email', 'spam_threshold') | ||||
|     for user_config in users: | ||||
|         if verbose: | ||||
|             print(str(user_config)) | ||||
|         localpart = user_config['localpart'] | ||||
|         domain_name = user_config['domain'] | ||||
|         password_hash = user_config.get('password_hash', None) | ||||
|         hash_scheme = user_config.get('hash_scheme', None) | ||||
|         domain = models.Domain.query.get(domain_name) | ||||
|         email = '{0}@{1}'.format(localpart, domain_name) | ||||
|         optional_params = {} | ||||
|         for k in user_optional_params: | ||||
|             if k in user_config: | ||||
|                 optional_params[k] = user_config[k] | ||||
|         if not domain: | ||||
|             domain = models.Domain(name=domain_name) | ||||
|             db.session.add(domain) | ||||
|         user = models.User.query.get(email) | ||||
|         tracked_users.add(email) | ||||
|         tracked_domains.add(domain_name) | ||||
|         if not user: | ||||
|             user = models.User( | ||||
|                 localpart=localpart, | ||||
|                 domain=domain, | ||||
|                 **optional_params | ||||
|             ) | ||||
|         else: | ||||
|             for k in optional_params: | ||||
|                 setattr(user, k, optional_params[k]) | ||||
|         user.set_password(password_hash, hash_scheme=hash_scheme, raw=True) | ||||
|         db.session.add(user) | ||||
| 
 | ||||
|     aliases = new_config.get('aliases', []) | ||||
|     tracked_aliases = set() | ||||
|     for alias_config in aliases: | ||||
|         if verbose: | ||||
|             print(str(alias_config)) | ||||
|         localpart = alias_config['localpart'] | ||||
|         domain_name = alias_config['domain'] | ||||
|         if type(alias_config['destination']) is str: | ||||
|             destination = alias_config['destination'].split(',') | ||||
|         else: | ||||
|             destination = alias_config['destination'] | ||||
|         wildcard = alias_config.get('wildcard', False) | ||||
|         domain = models.Domain.query.get(domain_name) | ||||
|         email = '{0}@{1}'.format(localpart, domain_name) | ||||
|         if not domain: | ||||
|             domain = models.Domain(name=domain_name) | ||||
|             db.session.add(domain) | ||||
|         alias = models.Alias.query.get(email) | ||||
|         tracked_aliases.add(email) | ||||
|         tracked_domains.add(domain_name) | ||||
|         if not alias: | ||||
|             alias = models.Alias( | ||||
|                 localpart=localpart, | ||||
|                 domain=domain, | ||||
|                 wildcard=wildcard, | ||||
|                 destination=destination, | ||||
|                 email=email | ||||
|             ) | ||||
|         else: | ||||
|             alias.destination = destination | ||||
|             alias.wildcard = wildcard | ||||
|         db.session.add(alias) | ||||
| 
 | ||||
|     db.session.commit() | ||||
| 
 | ||||
|     managers = new_config.get('managers', []) | ||||
|     # tracked_managers=set() | ||||
|     for manager_config in managers: | ||||
|         if verbose: | ||||
|             print(str(manager_config)) | ||||
|         domain_name = manager_config['domain'] | ||||
|         user_name = manager_config['user'] | ||||
|         domain = models.Domain.query.get(domain_name) | ||||
|         manageruser = models.User.query.get(user_name + '@' + domain_name) | ||||
|         if manageruser not in domain.managers: | ||||
|             domain.managers.append(manageruser) | ||||
|         db.session.add(domain) | ||||
| 
 | ||||
|     db.session.commit() | ||||
| 
 | ||||
|     if delete_objects: | ||||
|         for user in db.session.query(models.User).all(): | ||||
|             if not (user.email in tracked_users): | ||||
|                 if verbose: | ||||
|                     print("Deleting user: " + str(user.email)) | ||||
|                 db.session.delete(user) | ||||
|         for alias in db.session.query(models.Alias).all(): | ||||
|             if not (alias.email in tracked_aliases): | ||||
|                 if verbose: | ||||
|                     print("Deleting alias: " + str(alias.email)) | ||||
|                 db.session.delete(alias) | ||||
|         for domain in db.session.query(models.Domain).all(): | ||||
|             if not (domain.name in tracked_domains): | ||||
|                 if verbose: | ||||
|                     print("Deleting domain: " + str(domain.name)) | ||||
|                 db.session.delete(domain) | ||||
|     db.session.commit() | ||||
| 
 | ||||
| 
 | ||||
| @manager.command | ||||
| def user_delete(email): | ||||
|     """delete user""" | ||||
|     user = models.User.query.get(email) | ||||
|     if user: | ||||
|         db.session.delete(user) | ||||
|     db.session.commit() | ||||
| 
 | ||||
| 
 | ||||
| @manager.command | ||||
| def alias_delete(email): | ||||
|     """delete alias""" | ||||
|     alias = models.Alias.query.get(email) | ||||
|     if alias: | ||||
|         db.session.delete(alias) | ||||
|     db.session.commit() | ||||
| 
 | ||||
| 
 | ||||
| @manager.command | ||||
| def alias(localpart, domain_name, destination): | ||||
|     """ Create an alias | ||||
|     """ | ||||
|     domain = models.Domain.query.get(domain_name) | ||||
|     if not domain: | ||||
|         domain = models.Domain(name=domain_name) | ||||
|         db.session.add(domain) | ||||
|     alias = models.Alias( | ||||
|         localpart=localpart, | ||||
|         domain=domain, | ||||
|         destination=destination.split(','), | ||||
|         email="%s@%s" % (localpart, domain_name) | ||||
|     ) | ||||
|     db.session.add(alias) | ||||
|     db.session.commit() | ||||
| 
 | ||||
| # Set limits to a domain | ||||
| 
 | ||||
| 
 | ||||
| @manager.command | ||||
| def setlimits(domain_name, max_users, max_aliases, max_quota_bytes): | ||||
|     domain = models.Domain.query.get(domain_name) | ||||
|     domain.max_users = max_users | ||||
|     domain.max_aliases = max_aliases | ||||
|     domain.max_quota_bytes = max_quota_bytes | ||||
| 
 | ||||
|     db.session.add(domain) | ||||
|     db.session.commit() | ||||
| 
 | ||||
| # Make the user manager of a domain | ||||
| 
 | ||||
| 
 | ||||
| @manager.command | ||||
| def setmanager(domain_name, user_name='manager'): | ||||
|     domain = models.Domain.query.get(domain_name) | ||||
|     manageruser = models.User.query.get(user_name + '@' + domain_name) | ||||
|     domain.managers.append(manageruser) | ||||
|     db.session.add(domain) | ||||
|     db.session.commit() | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     manager.run() | ||||
| @ -0,0 +1,46 @@ | ||||
| import flask | ||||
| import flask_login | ||||
| import flask_script | ||||
| import flask_migrate | ||||
| import flask_babel | ||||
| import flask_limiter | ||||
| 
 | ||||
| 
 | ||||
| # Login configuration | ||||
| login = flask_login.LoginManager() | ||||
| login.login_view = "ui.login" | ||||
| login.user_loader(models.User.query.get) | ||||
| 
 | ||||
| @login_manager.unauthorized_handler | ||||
| def handle_needs_login(): | ||||
|     return flask.redirect( | ||||
|         flask.url_for('ui.login', next=flask.request.endpoint) | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| # Request rate limitation | ||||
| limiter = flask_limiter.Limiter(key_func=lambda: current_user.username) | ||||
| 
 | ||||
| 
 | ||||
| # Application translation | ||||
| babel = flask_babel.Babel() | ||||
| 
 | ||||
| @babel.localeselector | ||||
| def get_locale(): | ||||
|     translations = list(map(str, babel.list_translations())) | ||||
|     return flask.request.accept_languages.best_match(translations) | ||||
| 
 | ||||
| 
 | ||||
| # Proxy fixer | ||||
| class PrefixMiddleware(object): | ||||
|     def __call__(self, environ, start_response): | ||||
|         prefix = environ.get('HTTP_X_FORWARDED_PREFIX', '') | ||||
|         if prefix: | ||||
|             environ['SCRIPT_NAME'] = prefix | ||||
|         return self.app(environ, start_response) | ||||
| 
 | ||||
|     def init_app(self, app): | ||||
|         self.app = fixers.ProxyFix(app.wsgi_app) | ||||
|         app.wsgi_app = self | ||||
| 
 | ||||
| proxy = PrefixMiddleware() | ||||
					Loading…
					
					
				
		Reference in New Issue
	
	 kaiyou
						kaiyou