feat(dashboard): Display system version and Git hash, link to release tag #19

This commit is contained in:
rustmailer
2025-11-27 03:55:20 +08:00
parent 855c9a1ffc
commit 736270b08c
20 changed files with 77 additions and 26 deletions
+5 -1
View File
@@ -16,12 +16,12 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use poem_openapi::Object;
use serde::{Deserialize, Serialize};
use tantivy::{schema::Value, TantivyDocument};
use crate::{
bichon_version,
modules::{
account::migration::AccountModel,
error::{code::ErrorCode, BichonResult},
@@ -45,6 +45,8 @@ pub struct DashboardStats {
pub with_attachment_count: u64, // Emails with attachments
pub without_attachment_count: u64, // Emails without attachments
pub top_largest_emails: Vec<LargestEmail>, // Top 10 largest emails
pub system_version: String, // The semantic version string of the currently running backend service
pub commit_hash: String, // Git commit hash used to build this system version
}
impl DashboardStats {
@@ -57,6 +59,8 @@ impl DashboardStats {
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
stat.index_usage_bytes = get_total_size(&DATA_DIR_MANAGER.envelope_dir)
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?;
stat.system_version = bichon_version!().to_string();
stat.commit_hash = env!("GIT_HASH").to_string();
Ok(stat)
}
}
+2
View File
@@ -54,6 +54,8 @@ export interface DashboardStats {
with_attachment_count: number; // Emails with attachments
without_attachment_count: number; // Emails without attachments
top_largest_emails: LargestEmail[]; // Top 10 largest emails
system_version: string, //The semantic version string of the currently running backend service
commit_hash: string //Git commit hash used to build this system version
}
export interface TimeBucket {
+36 -8
View File
@@ -10,11 +10,11 @@
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
@@ -22,7 +22,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import { Skeleton } from '@/components/ui/skeleton';
import { XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, PieChart, Pie, Cell, BarChart, Bar } from 'recharts';
import { Mail, HardDrive, Database, Users, Inbox } from 'lucide-react';
import { Mail, HardDrive, Database, Users, Inbox, Info } from 'lucide-react';
import { formatBytes, formatNumber } from '@/lib/utils';
import { useQuery } from '@tanstack/react-query';
import { get_dashboard_stats, TimeBucket } from '@/api/system/api';
@@ -94,7 +94,7 @@ export default function MailArchiveDashboard() {
queryFn: get_dashboard_stats,
});
const { t } = useTranslation();
const { t } = useTranslation();
const totalAttachments = (stats?.with_attachment_count ?? 0) + (stats?.without_attachment_count ?? 0);
const attachmentRatio = totalAttachments > 0 ? (stats?.with_attachment_count ?? 0) / totalAttachments : 0;
@@ -123,8 +123,8 @@ export default function MailArchiveDashboard() {
<Skeleton className="h-7 w-28 rounded-full" />
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-5">
{[...Array(5)].map((_, i) => (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-6">
{[...Array(6)].map((_, i) => (
<MetricCardSkeleton key={i} />
))}
</div>
@@ -156,8 +156,8 @@ export default function MailArchiveDashboard() {
</div>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-5">
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-6">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">{t('dashboard.mailAccounts')}</CardTitle>
@@ -212,6 +212,29 @@ export default function MailArchiveDashboard() {
<p className="text-xs text-muted-foreground">{t('dashboard.tantivyIndex')}</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">{t('dashboard.systemVersion')}</CardTitle>
<Info className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className='text-2xl font-bold'>
{stats!.system_version ? (
<a
href={`https://github.com/rustmailer/bichon/releases/tag/${stats!.system_version}`}
target="_blank"
rel="noopener noreferrer"
className="hover:underline"
>
{stats!.system_version}
</a>
) : 'N/A'}
</div>
<p className="text-xs text-muted-foreground">
{stats!.commit_hash ?? 'N/A'}
</p>
</CardContent>
</Card>
</div>
<Tabs defaultValue="trend" className="space-y-4">
@@ -406,6 +429,11 @@ export default function MailArchiveDashboard() {
</TabsContent>
</Tabs>
</div>
{/* Footer / Copyright - New Addition */}
<div className="p-6 md:p-8 pt-0 text-center text-xs text-muted-foreground">
© 2025 <a href="https://rustmailer.com" target="_blank" rel="noopener noreferrer" className="hover:underline">rustmailer.com</a> - Bichon Email Archiving Project
</div>
</Main>
</>
);
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "لا يوجد نشاط حديث",
"noSendersData": "لا توجد بيانات للمرسلين",
"noLargeEmails": "لا توجد رسائل بريد إلكتروني كبيرة",
"noAccountData": "لا توجد بيانات للحساب"
"noAccountData": "لا توجد بيانات للحساب",
"systemVersion": "إصدار النظام"
},
"accounts": {
"title": "حسابات البريد الإلكتروني",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "Ingen nylig aktivitet",
"noSendersData": "Ingen afsenderdata",
"noLargeEmails": "Ingen store e-mails",
"noAccountData": "Ingen kontodata"
"noAccountData": "Ingen kontodata",
"systemVersion": "Systemversion"
},
"accounts": {
"title": "E-mailkonti",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "Keine kürzliche Aktivität",
"noSendersData": "Keine Absenderdaten",
"noLargeEmails": "Keine großen E-Mails",
"noAccountData": "Keine Kontodaten"
"noAccountData": "Keine Kontodaten",
"systemVersion": "Systemversion"
},
"accounts": {
"title": "E-Mail-Konten",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "No recent activity",
"noSendersData": "No senders data",
"noLargeEmails": "No large emails",
"noAccountData": "No account data"
"noAccountData": "No account data",
"systemVersion": "System Version"
},
"accounts": {
"title": "Email Accounts",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "Sin actividad reciente",
"noSendersData": "Sin datos de remitentes",
"noLargeEmails": "Sin correos grandes",
"noAccountData": "Sin datos de cuenta"
"noAccountData": "Sin datos de cuenta",
"systemVersion": "Versión del sistema"
},
"accounts": {
"title": "Cuentas de correo",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "Ei viimeaikaisia tapahtumia",
"noSendersData": "Ei lähettäjätietoja",
"noLargeEmails": "Ei suuria sähköposteja",
"noAccountData": "Ei tilitietoja"
"noAccountData": "Ei tilitietoja",
"systemVersion": "Järjestelmäversio"
},
"accounts": {
"title": "Sähköpostitilit",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "Aucune activité récente",
"noSendersData": "Aucune donnée d'expéditeurs",
"noLargeEmails": "Aucun gros e-mail",
"noAccountData": "Aucune donnée de compte"
"noAccountData": "Aucune donnée de compte",
"systemVersion": "Version du système"
},
"accounts": {
"title": "Comptes de Messagerie",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "Nessuna attività recente",
"noSendersData": "Nessun dato sui mittenti",
"noLargeEmails": "Nessuna email di grandi dimensioni",
"noAccountData": "Nessun dato sull'account"
"noAccountData": "Nessun dato sull'account",
"systemVersion": "Versione del sistema"
},
"accounts": {
"title": "Account Email",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "最近のアクティビティなし",
"noSendersData": "送信者データなし",
"noLargeEmails": "サイズの大きいメールなし",
"noAccountData": "アカウントデータなし"
"noAccountData": "アカウントデータなし",
"systemVersion": "システムバージョン"
},
"accounts": {
"title": "メールアカウント",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "최근 활동 없음",
"noSendersData": "발신자 데이터 없음",
"noLargeEmails": "크기가 큰 이메일 없음",
"noAccountData": "계정 데이터 없음"
"noAccountData": "계정 데이터 없음",
"systemVersion": "시스템 버전"
},
"accounts": {
"title": "이메일 계정",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "Geen recente activiteit",
"noSendersData": "Geen afzendersgegevens",
"noLargeEmails": "Geen grote e-mails",
"noAccountData": "Geen accountgegevens"
"noAccountData": "Geen accountgegevens",
"systemVersion": "Systeemversie"
},
"accounts": {
"title": "E-mailaccounts",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "Ingen nylig aktivitet",
"noSendersData": "Ingen avsenderdata",
"noLargeEmails": "Ingen store e-poster",
"noAccountData": "Ingen kontodata"
"noAccountData": "Ingen kontodata",
"systemVersion": "Systemversjon"
},
"accounts": {
"title": "E-postkontoer",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "Nenhuma Atividade Recente",
"noSendersData": "Sem Dados de Remetentes",
"noLargeEmails": "Sem Emails Grandes",
"noAccountData": "Sem Dados de Contas"
"noAccountData": "Sem Dados de Contas",
"systemVersion": "Versão do sistema"
},
"accounts": {
"title": "Contas de Email",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "Нет недавней активности",
"noSendersData": "Нет данных об отправителях",
"noLargeEmails": "Нет больших писем",
"noAccountData": "Нет данных об аккаунте"
"noAccountData": "Нет данных об аккаунте",
"systemVersion": "Версия системы"
},
"accounts": {
"title": "Почтовые учетные записи",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "Ingen nylig aktivitet",
"noSendersData": "Inga avsändardata",
"noLargeEmails": "Inga stora e-postmeddelanden",
"noAccountData": "Inga kontodata"
"noAccountData": "Inga kontodata",
"systemVersion": "Systemversion"
},
"accounts": {
"title": "E-postkonton",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "無近期活動",
"noSendersData": "無寄件人資料",
"noLargeEmails": "無大容量郵件",
"noAccountData": "無帳號資料"
"noAccountData": "無帳號資料",
"systemVersion": "系統版本"
},
"accounts": {
"title": "郵件帳號",
+2 -1
View File
@@ -126,7 +126,8 @@
"noRecentActivity": "无最近活动",
"noSendersData": "无发件人数据",
"noLargeEmails": "无大邮件",
"noAccountData": "无账户数据"
"noAccountData": "无账户数据",
"systemVersion": "系统版本"
},
"accounts": {
"title": "邮件账户",