Usage Statistics
-
+
@UserUsageStats.TotalCredentials.ToString("N0")
Total Credentials
@@ -60,6 +60,14 @@ else
(+@UserUsageStats.RecentReceivedEmails72h.ToString("N0") in last 72h)
}
+
+
@UserUsageStats.TotalEmailAttachments.ToString("N0")
+
Email Attachments
+ @if (UserUsageStats.TotalEmailAttachmentStorageMB > 0)
+ {
+
@UserUsageStats.TotalEmailAttachmentStorageMB.ToString("N2") MB
+ }
+
}
diff --git a/apps/server/AliasVault.Admin/Services/StatisticsService.cs b/apps/server/AliasVault.Admin/Services/StatisticsService.cs
index 127392f0b..b1a66c3fa 100644
--- a/apps/server/AliasVault.Admin/Services/StatisticsService.cs
+++ b/apps/server/AliasVault.Admin/Services/StatisticsService.cs
@@ -167,6 +167,15 @@ public class StatisticsService
.Where(e => e.EncryptionKey.UserId == userId && e.DateSystem >= cutoffDate)
.CountAsync();
+ // Get email attachment statistics (all-time)
+ var emailAttachmentQuery = context.EmailAttachments
+ .Where(a => a.Email.EncryptionKey.UserId == userId);
+
+ stats.TotalEmailAttachments = await emailAttachmentQuery.CountAsync();
+ stats.TotalEmailAttachmentStorage = stats.TotalEmailAttachments > 0
+ ? await emailAttachmentQuery.SumAsync(a => (long)a.Filesize)
+ : 0L;
+
return stats;
}
diff --git a/apps/server/AliasVault.Admin/wwwroot/css/tailwind.css b/apps/server/AliasVault.Admin/wwwroot/css/tailwind.css
index 68b1651f4..0d40973c3 100644
--- a/apps/server/AliasVault.Admin/wwwroot/css/tailwind.css
+++ b/apps/server/AliasVault.Admin/wwwroot/css/tailwind.css
@@ -759,6 +759,10 @@ video {
margin-top: 2rem;
}
+.mb-8 {
+ margin-bottom: 2rem;
+}
+
.line-clamp-1 {
overflow: hidden;
display: -webkit-box;
@@ -918,6 +922,14 @@ video {
width: 100%;
}
+.w-24 {
+ width: 6rem;
+}
+
+.w-32 {
+ width: 8rem;
+}
+
.max-w-2xl {
max-width: 42rem;
}
@@ -1187,6 +1199,10 @@ video {
border-left-width: 4px;
}
+.border-t {
+ border-top-width: 1px;
+}
+
.border-gray-200 {
--tw-border-opacity: 1;
border-color: rgb(229 231 235 / var(--tw-border-opacity));
@@ -1402,6 +1418,11 @@ video {
background-color: rgb(234 179 8 / var(--tw-bg-opacity));
}
+.bg-purple-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(243 232 255 / var(--tw-bg-opacity));
+}
+
.bg-opacity-50 {
--tw-bg-opacity: 0.5;
}
@@ -1430,6 +1451,10 @@ video {
padding: 1.5rem;
}
+.p-8 {
+ padding: 2rem;
+}
+
.px-1 {
padding-left: 0.25rem;
padding-right: 0.25rem;
@@ -1637,6 +1662,10 @@ video {
font-weight: 600;
}
+.uppercase {
+ text-transform: uppercase;
+}
+
.italic {
font-style: italic;
}
@@ -1764,6 +1793,11 @@ video {
color: rgb(34 197 94 / var(--tw-text-opacity));
}
+.text-blue-500 {
+ --tw-text-opacity: 1;
+ color: rgb(59 130 246 / var(--tw-text-opacity));
+}
+
.underline {
text-decoration-line: underline;
}
@@ -1923,6 +1957,16 @@ video {
color: rgb(154 93 38 / var(--tw-text-opacity));
}
+.hover\:text-blue-800:hover {
+ --tw-text-opacity: 1;
+ color: rgb(30 64 175 / var(--tw-text-opacity));
+}
+
+.hover\:text-red-900:hover {
+ --tw-text-opacity: 1;
+ color: rgb(127 29 29 / var(--tw-text-opacity));
+}
+
.hover\:underline:hover {
text-decoration-line: underline;
}
@@ -2152,6 +2196,15 @@ video {
background-color: rgb(17 24 39 / 0.5);
}
+.dark\:bg-purple-900:is(.dark *) {
+ --tw-bg-opacity: 1;
+ background-color: rgb(88 28 135 / var(--tw-bg-opacity));
+}
+
+.dark\:bg-red-800\/10:is(.dark *) {
+ background-color: rgb(153 27 27 / 0.1);
+}
+
.dark\:bg-opacity-80:is(.dark *) {
--tw-bg-opacity: 0.8;
}
@@ -2256,6 +2309,16 @@ video {
color: rgb(254 240 138 / var(--tw-text-opacity));
}
+.dark\:text-gray-500:is(.dark *) {
+ --tw-text-opacity: 1;
+ color: rgb(107 114 128 / var(--tw-text-opacity));
+}
+
+.dark\:text-purple-300:is(.dark *) {
+ --tw-text-opacity: 1;
+ color: rgb(216 180 254 / var(--tw-text-opacity));
+}
+
.dark\:placeholder-gray-400:is(.dark *)::-moz-placeholder {
--tw-placeholder-opacity: 1;
color: rgb(156 163 175 / var(--tw-placeholder-opacity));
@@ -2320,6 +2383,16 @@ video {
color: rgb(255 255 255 / var(--tw-text-opacity));
}
+.dark\:hover\:text-blue-300:hover:is(.dark *) {
+ --tw-text-opacity: 1;
+ color: rgb(147 197 253 / var(--tw-text-opacity));
+}
+
+.dark\:hover\:text-red-300:hover:is(.dark *) {
+ --tw-text-opacity: 1;
+ color: rgb(252 165 165 / var(--tw-text-opacity));
+}
+
.dark\:focus\:border-blue-500:focus:is(.dark *) {
--tw-border-opacity: 1;
border-color: rgb(59 130 246 / var(--tw-border-opacity));
@@ -2560,6 +2633,10 @@ video {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
+ .lg\:grid-cols-3 {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ }
+
.lg\:flex-row {
flex-direction: row;
}