1
0
mirror of https://github.com/mail-in-a-box/mailinabox.git synced 2024-11-22 02:17:26 +00:00

in the admin, show user mailbox sizes, fixes #210

This commit is contained in:
Joshua Tauberer 2014-10-07 20:24:11 +00:00
parent 443b084a17
commit 06a8ce1c9d
5 changed files with 72 additions and 23 deletions

View File

@ -98,7 +98,7 @@ def me():
@authorized_personnel_only @authorized_personnel_only
def mail_users(): def mail_users():
if request.args.get("format", "") == "json": if request.args.get("format", "") == "json":
return json_response(get_mail_users_ex(env, with_archived=True)) return json_response(get_mail_users_ex(env, with_archived=True, with_slow_info=True))
else: else:
return "".join(x+"\n" for x in get_mail_users(env)) return "".join(x+"\n" for x in get_mail_users(env))

View File

@ -53,7 +53,7 @@ def get_mail_users(env):
users = [ row[0] for row in c.fetchall() ] users = [ row[0] for row in c.fetchall() ]
return utils.sort_email_addresses(users, env) return utils.sort_email_addresses(users, env)
def get_mail_users_ex(env, with_archived=False): def get_mail_users_ex(env, with_archived=False, with_slow_info=False):
# Returns a complex data structure of all user accounts, optionally # Returns a complex data structure of all user accounts, optionally
# including archived (status="inactive") accounts. # including archived (status="inactive") accounts.
# #
@ -86,15 +86,20 @@ def get_mail_users_ex(env, with_archived=False):
c.execute('SELECT email, privileges FROM users') c.execute('SELECT email, privileges FROM users')
for email, privileges in c.fetchall(): for email, privileges in c.fetchall():
active_accounts.add(email) active_accounts.add(email)
users.append({
user = {
"email": email, "email": email,
"privileges": parse_privs(privileges), "privileges": parse_privs(privileges),
"status": "active", "status": "active",
"aliases": [ }
users.append(user)
if with_slow_info:
user["aliases"] = [
(alias, sorted(evaluate_mail_alias_map(alias, aliases, env))) (alias, sorted(evaluate_mail_alias_map(alias, aliases, env)))
for alias in aliases.get(email.lower(), []) for alias in aliases.get(email.lower(), [])
] ]
}) user["mailbox_size"] = utils.du(os.path.join(env['STORAGE_ROOT'], 'mail/mailboxes', *reversed(email.split("@"))))
# Add in archived accounts. # Add in archived accounts.
if with_archived: if with_archived:
@ -102,13 +107,17 @@ def get_mail_users_ex(env, with_archived=False):
for domain in os.listdir(root): for domain in os.listdir(root):
for user in os.listdir(os.path.join(root, domain)): for user in os.listdir(os.path.join(root, domain)):
email = user + "@" + domain email = user + "@" + domain
mbox = os.path.join(root, domain, user)
if email in active_accounts: continue if email in active_accounts: continue
users.append({ user = {
"email": email, "email": email,
"privileges": "", "privileges": "",
"status": "inactive", "status": "inactive",
"mailbox": os.path.join(root, domain, user), "mailbox": mbox,
}) }
users.append(user)
if with_slow_info:
user["mailbox_size"] = utils.du(mbox)
# Group by domain. # Group by domain.
domains = { } domains = { }

View File

@ -39,7 +39,12 @@ function nice_size(bytes) {
bytes /= 1024; bytes /= 1024;
powers.shift(); powers.shift();
} }
return (Math.round(bytes*10)/10) + " " + powers[0]; // round to have three significant figures but at most one decimal place
if (bytes >= 100)
bytes = Math.round(bytes)
else
bytes = Math.round(bytes*10)/10;
return bytes + " " + powers[0];
} }
function show_system_backup() { function show_system_backup() {

View File

@ -2,7 +2,7 @@
<style> <style>
#user_table tr.account_inactive td.address { color: #888; text-decoration: line-through; } #user_table tr.account_inactive td.address { color: #888; text-decoration: line-through; }
#user_table .aliases { margin-top: .33em; font-size: 95%; } #user_table .aliases { font-size: 90%; }
#user_table .aliases div:before { content: "⇖ "; } #user_table .aliases div:before { content: "⇖ "; }
#user_table .aliases div { } #user_table .aliases div { }
#user_table .actions { margin-top: .33em; font-size: 95%; } #user_table .actions { margin-top: .33em; font-size: 95%; }
@ -38,6 +38,13 @@
<h3>Existing mail users</h3> <h3>Existing mail users</h3>
<table id="user_table" class="table" style="width: auto"> <table id="user_table" class="table" style="width: auto">
<thead>
<tr>
<th width="50%">Email Address</th>
<th>Actions</th>
<th>Mailbox Size</th>
</tr>
</thead>
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
@ -45,10 +52,9 @@
<div style="display: none"> <div style="display: none">
<table> <table>
<tr id="user-template"> <tr id="user-template">
<td class='email'> <td class='address'>
<div class='address'> </div> </td>
<td class='actions'>
<div class='actions'>
<span class='privs'> <span class='privs'>
</span> </span>
@ -65,9 +71,13 @@
<a href="#" onclick="users_remove(this); return false;" class='if_active' title="Archive Account"> <a href="#" onclick="users_remove(this); return false;" class='if_active' title="Archive Account">
archive account archive account
</a> </a>
</td>
<td class='mailboxsize'>
</td>
</tr>
<tr id="user-extra-template">
<td colspan="3" style="border-top: 0; padding-top: 0">
<div class='if_inactive restore_info' style='color: #888; font-size: 90%'>To restore account, create a new account with this email address. Or to permanently delete the mailbox, delete the directory <tt></tt> on the machine.</div> <div class='if_inactive restore_info' style='color: #888; font-size: 90%'>To restore account, create a new account with this email address. Or to permanently delete the mailbox, delete the directory <tt></tt> on the machine.</div>
</div>
<div class='aliases' style='display: none'> </div> <div class='aliases' style='display: none'> </div>
</td> </td>
@ -94,13 +104,19 @@ function show_users() {
var user = r[i].users[k]; var user = r[i].users[k];
var n = $("#user-template").clone(); var n = $("#user-template").clone();
var n2 = $("#user-extra-template").clone();
n.attr('id', ''); n.attr('id', '');
n2.attr('id', '');
$('#user_table tbody').append(n);
$('#user_table tbody').append(n2);
n.addClass("account_" + user.status); n.addClass("account_" + user.status);
n2.addClass("account_" + user.status);
n.attr('data-email', user.email); n.attr('data-email', user.email);
n.find('td.email .address').text(user.email) n.find('.address').text(user.email)
$('#user_table tbody').append(n); n.find('.mailboxsize').text(nice_size(user.mailbox_size))
n.find('.restore_info tt').text(user.mailbox); n2.find('.restore_info tt').text(user.mailbox);
if (user.status == 'inactive') continue; if (user.status == 'inactive') continue;
@ -121,9 +137,9 @@ function show_users() {
} }
if (user.aliases && user.aliases.length > 0) { if (user.aliases && user.aliases.length > 0) {
n.find('.aliases').show(); n2.find('.aliases').show();
for (var j = 0; j < user.aliases.length; j++) { for (var j = 0; j < user.aliases.length; j++) {
n.find('td.email .aliases').append($("<div/>").text( n2.find('.aliases').append($("<div/>").text(
user.aliases[j][0] user.aliases[j][0]
+ (user.aliases[j][1].length > 0 ? " ⇐ " + user.aliases[j][1].join(", ") : "") + (user.aliases[j][1].length > 0 ? " ⇐ " + user.aliases[j][1].join(", ") : "")
)) ))

View File

@ -165,3 +165,22 @@ def create_syslog_handler():
handler = logging.handlers.SysLogHandler(address='/dev/log') handler = logging.handlers.SysLogHandler(address='/dev/log')
handler.setLevel(logging.WARNING) handler.setLevel(logging.WARNING)
return handler return handler
def du(path):
# Computes the size of all files in the path, like the `du` command.
# Based on http://stackoverflow.com/a/17936789. Takes into account
# soft and hard links.
total_size = 0
seen = set()
for dirpath, dirnames, filenames in os.walk(path):
for f in filenames:
fp = os.path.join(dirpath, f)
try:
stat = os.lstat(fp)
except OSError:
continue
if stat.st_ino in seen:
continue
seen.add(stat.st_ino)
total_size += stat.st_size
return total_size