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