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
def mail_users():
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:
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() ]
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
# 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')
for email, privileges in c.fetchall():
active_accounts.add(email)
users.append({
user = {
"email": email,
"privileges": parse_privs(privileges),
"status": "active",
"aliases": [
}
users.append(user)
if with_slow_info:
user["aliases"] = [
(alias, sorted(evaluate_mail_alias_map(alias, aliases, env)))
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.
if with_archived:
@ -102,13 +107,17 @@ def get_mail_users_ex(env, with_archived=False):
for domain in os.listdir(root):
for user in os.listdir(os.path.join(root, domain)):
email = user + "@" + domain
mbox = os.path.join(root, domain, user)
if email in active_accounts: continue
users.append({
user = {
"email": email,
"privileges": "",
"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.
domains = { }

View File

@ -39,7 +39,12 @@ function nice_size(bytes) {
bytes /= 1024;
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() {

View File

@ -1,8 +1,8 @@
<h2>Users</h2>
<style>
#user_table tr.account_inactive td .address { color: #888; text-decoration: line-through; }
#user_table .aliases { margin-top: .33em; font-size: 95%; }
#user_table tr.account_inactive td.address { color: #888; text-decoration: line-through; }
#user_table .aliases { font-size: 90%; }
#user_table .aliases div:before { content: "⇖ "; }
#user_table .aliases div { }
#user_table .actions { margin-top: .33em; font-size: 95%; }
@ -38,6 +38,13 @@
<h3>Existing mail users</h3>
<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>
</table>
@ -45,10 +52,9 @@
<div style="display: none">
<table>
<tr id="user-template">
<td class='email'>
<div class='address'> </div>
<div class='actions'>
<td class='address'>
</td>
<td class='actions'>
<span class='privs'>
</span>
@ -65,9 +71,13 @@
<a href="#" onclick="users_remove(this); return false;" class='if_active' title="Archive Account">
archive account
</a>
<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>
</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='aliases' style='display: none'> </div>
</td>
@ -94,13 +104,19 @@ function show_users() {
var user = r[i].users[k];
var n = $("#user-template").clone();
var n2 = $("#user-extra-template").clone();
n.attr('id', '');
n2.attr('id', '');
$('#user_table tbody').append(n);
$('#user_table tbody').append(n2);
n.addClass("account_" + user.status);
n2.addClass("account_" + user.status);
n.attr('data-email', user.email);
n.find('td.email .address').text(user.email)
$('#user_table tbody').append(n);
n.find('.restore_info tt').text(user.mailbox);
n.find('.address').text(user.email)
n.find('.mailboxsize').text(nice_size(user.mailbox_size))
n2.find('.restore_info tt').text(user.mailbox);
if (user.status == 'inactive') continue;
@ -121,9 +137,9 @@ function show_users() {
}
if (user.aliases && user.aliases.length > 0) {
n.find('.aliases').show();
n2.find('.aliases').show();
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][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.setLevel(logging.WARNING)
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