mirror of
				https://github.com/optim-enterprises-bv/Mailu.git
				synced 2025-10-31 01:57:59 +00:00 
			
		
		
		
	Display the user quota in the admin interface
This commit is contained in:
		| @@ -11,6 +11,7 @@ import os | |||||||
| import docker | import docker | ||||||
| import socket | import socket | ||||||
| import uuid | import uuid | ||||||
|  | import redis | ||||||
|  |  | ||||||
| from werkzeug.contrib import fixers | from werkzeug.contrib import fixers | ||||||
|  |  | ||||||
| @@ -26,6 +27,7 @@ default_config = { | |||||||
|     'BABEL_DEFAULT_TIMEZONE': 'UTC', |     'BABEL_DEFAULT_TIMEZONE': 'UTC', | ||||||
|     'BOOTSTRAP_SERVE_LOCAL': True, |     'BOOTSTRAP_SERVE_LOCAL': True, | ||||||
|     'RATELIMIT_STORAGE_URL': 'redis://redis/2', |     'RATELIMIT_STORAGE_URL': 'redis://redis/2', | ||||||
|  |     'QUOTA_STORAGE_URL': 'redis://redis/1', | ||||||
|     'DEBUG': False, |     'DEBUG': False, | ||||||
|     'DOMAIN_REGISTRATION': False, |     'DOMAIN_REGISTRATION': False, | ||||||
|     # Statistics management |     # Statistics management | ||||||
| @@ -87,6 +89,9 @@ manager.add_command('db', flask_migrate.MigrateCommand) | |||||||
| babel = flask_babel.Babel(app) | babel = flask_babel.Babel(app) | ||||||
| translations = list(map(str, babel.list_translations())) | translations = list(map(str, babel.list_translations())) | ||||||
|  |  | ||||||
|  | # Quota manager | ||||||
|  | quota = redis.Redis.from_url(app.config.get("QUOTA_STORAGE_URL")) | ||||||
|  |  | ||||||
| @babel.localeselector | @babel.localeselector | ||||||
| def get_locale(): | def get_locale(): | ||||||
|     return flask.request.accept_languages.best_match(translations) |     return flask.request.accept_languages.best_match(translations) | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| from mailu import app, db, dkim, login_manager | from mailu import app, db, dkim, login_manager, quota | ||||||
|  |  | ||||||
| from sqlalchemy.ext import declarative | from sqlalchemy.ext import declarative | ||||||
| from passlib import context, hash | from passlib import context, hash | ||||||
| @@ -20,9 +20,8 @@ class IdnaDomain(db.TypeDecorator): | |||||||
|  |  | ||||||
|     impl = db.String(80) |     impl = db.String(80) | ||||||
|  |  | ||||||
|  |  | ||||||
|     def process_bind_param(self, value, dialect): |     def process_bind_param(self, value, dialect): | ||||||
|         return idna.encode(value) |         return idna.encode(value).decode("ascii") | ||||||
|  |  | ||||||
|     def process_result_value(self, value, dialect): |     def process_result_value(self, value, dialect): | ||||||
|         return idna.decode(value) |         return idna.decode(value) | ||||||
| @@ -34,31 +33,19 @@ class IdnaEmail(db.TypeDecorator): | |||||||
|  |  | ||||||
|     impl = db.String(255, collation="NOCASE") |     impl = db.String(255, collation="NOCASE") | ||||||
|  |  | ||||||
|  |  | ||||||
|     def process_bind_param(self, value, dialect): |     def process_bind_param(self, value, dialect): | ||||||
|         localpart, domain_name = value.split('@') |         localpart, domain_name = value.split('@') | ||||||
|  |         return "{0}@{1}".format( | ||||||
|         email = "{0}@{1}".format( |  | ||||||
|             localpart, |             localpart, | ||||||
|             idna.encode(domain_name).decode('ascii'), |             idna.encode(domain_name).decode('ascii'), | ||||||
|         ) |         ) | ||||||
|         return email |  | ||||||
|  |  | ||||||
|     def process_result_value(self, value, dialect): |     def process_result_value(self, value, dialect): | ||||||
|         localpart, domain_name = value.split('@') |         localpart, domain_name = value.split('@') | ||||||
|  |         return "{0}@{1}".format( | ||||||
|         email = "{0}@{1}".format( |  | ||||||
|             localpart, |             localpart, | ||||||
|             idna.decode(domain_name), |             idna.decode(domain_name), | ||||||
|         ) |         ) | ||||||
|         return email |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # Many-to-many association table for domain managers |  | ||||||
| managers = db.Table('manager', |  | ||||||
|     db.Column('domain_name', IdnaDomain, db.ForeignKey('domain.name')), |  | ||||||
|     db.Column('user_email', IdnaEmail, db.ForeignKey('user.email')) |  | ||||||
| ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class CommaSeparatedList(db.TypeDecorator): | class CommaSeparatedList(db.TypeDecorator): | ||||||
| @@ -67,7 +54,6 @@ class CommaSeparatedList(db.TypeDecorator): | |||||||
|  |  | ||||||
|     impl = db.String |     impl = db.String | ||||||
|  |  | ||||||
|  |  | ||||||
|     def process_bind_param(self, value, dialect): |     def process_bind_param(self, value, dialect): | ||||||
|         if type(value) is not list: |         if type(value) is not list: | ||||||
|             raise TypeError("Shoud be a list") |             raise TypeError("Shoud be a list") | ||||||
| @@ -80,6 +66,13 @@ class CommaSeparatedList(db.TypeDecorator): | |||||||
|         return filter(bool, value.split(",")) |         return filter(bool, value.split(",")) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Many-to-many association table for domain managers | ||||||
|  | managers = db.Table('manager', | ||||||
|  |     db.Column('domain_name', IdnaDomain, db.ForeignKey('domain.name')), | ||||||
|  |     db.Column('user_email', IdnaEmail, db.ForeignKey('user.email')) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| class Base(db.Model): | class Base(db.Model): | ||||||
|     """ Base class for all models |     """ Base class for all models | ||||||
|     """ |     """ | ||||||
| @@ -264,6 +257,10 @@ class User(Base, Email): | |||||||
|     def get_id(self): |     def get_id(self): | ||||||
|         return self.email |         return self.email | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def quota_bytes_used(self): | ||||||
|  |         return quota.get(self.email) or 0 | ||||||
|  |  | ||||||
|     scheme_dict = {'SHA512-CRYPT': "sha512_crypt", |     scheme_dict = {'SHA512-CRYPT': "sha512_crypt", | ||||||
|                    'SHA256-CRYPT': "sha256_crypt", |                    'SHA256-CRYPT': "sha256_crypt", | ||||||
|                    'MD5-CRYPT': "md5_crypt", |                    'MD5-CRYPT': "md5_crypt", | ||||||
|   | |||||||
| @@ -40,7 +40,7 @@ | |||||||
|     {% if user.enable_imap %}<span class="label label-info">imap</span>{% endif %} |     {% if user.enable_imap %}<span class="label label-info">imap</span>{% endif %} | ||||||
|     {% if user.enable_pop %}<span class="label label-info">pop3</span>{% endif %} |     {% if user.enable_pop %}<span class="label label-info">pop3</span>{% endif %} | ||||||
|   </td> |   </td> | ||||||
|   <td>{{ (user.quota_bytes | filesizeformat) if user.quota_bytes else '∞' }}</td> |   <td>{{ user.quota_bytes_used | filesizeformat }} / {{ (user.quota_bytes | filesizeformat) if user.quota_bytes else '∞' }}</td> | ||||||
|   <td>{{ user.comment or '-' }}</td> |   <td>{{ user.comment or '-' }}</td> | ||||||
|   <td>{{ user.created_at }}</td> |   <td>{{ user.created_at }}</td> | ||||||
|   <td>{{ user.updated_at or '' }}</td> |   <td>{{ user.updated_at or '' }}</td> | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ log_path = /dev/stderr | |||||||
| protocols = imap pop3 lmtp sieve | protocols = imap pop3 lmtp sieve | ||||||
| postmaster_address = {{ POSTMASTER }}@{{ DOMAIN }} | postmaster_address = {{ POSTMASTER }}@{{ DOMAIN }} | ||||||
| hostname = {{ HOSTNAMES.split(",")[0] }} | hostname = {{ HOSTNAMES.split(",")[0] }} | ||||||
| mail_plugins = $mail_plugins quota | mail_plugins = $mail_plugins quota quota_clone | ||||||
| submission_host = front | submission_host = front | ||||||
|  |  | ||||||
| service dict { | service dict { | ||||||
| @@ -119,6 +119,7 @@ service lmtp { | |||||||
|  |  | ||||||
| plugin { | plugin { | ||||||
|   quota = maildir:User quota |   quota = maildir:User quota | ||||||
|  |   quota_clone_dict = redis:host={{ REDIS_ADDRESS }}:db=1 | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ convert = lambda src, dst: open(dst, "w").write(jinja2.Template(open(src).read() | |||||||
|  |  | ||||||
| # Actual startup script | # Actual startup script | ||||||
| os.environ["FRONT_ADDRESS"] = socket.gethostbyname("front") | os.environ["FRONT_ADDRESS"] = socket.gethostbyname("front") | ||||||
|  | os.environ["REDIS_ADDRESS"] = socket.gethostbyname("redis") | ||||||
| if os.environ["WEBMAIL"] != "none": | if os.environ["WEBMAIL"] != "none": | ||||||
| 	os.environ["WEBMAIL_ADDRESS"] = socket.gethostbyname("webmail") | 	os.environ["WEBMAIL_ADDRESS"] = socket.gethostbyname("webmail") | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 kaiyou
					kaiyou