in the admin, show user mailbox sizes, fixes #210
This commit is contained in:
parent
443b084a17
commit
06a8ce1c9d
|
@ -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))
|
||||
|
||||
|
|
|
@ -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 = { }
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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(", ") : "")
|
||||
))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue