domain_health_status
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
FiRefreshCw, FiList, FiLogOut, FiSettings, FiKey, FiTrash2, FiPlus, FiInbox,
|
||||
FiUsers, FiUser, FiHardDrive, FiDollarSign,
|
||||
FiUsers, FiUser, FiHardDrive, FiDollarSign, FiActivity,
|
||||
} from 'react-icons/fi';
|
||||
|
||||
import Login from './components/Login';
|
||||
@@ -17,8 +17,10 @@ import AdminUsersModal from './components/AdminUsersModal';
|
||||
import ChangeMyPasswordModal from './components/ChangeMyPasswordModal';
|
||||
import DomainQuotaModal from './components/DomainQuotaModal';
|
||||
import BillingModal from './components/BillingModal';
|
||||
import HealthModal from './components/HealthModal';
|
||||
import HealthBanner from './components/HealthBanner';
|
||||
|
||||
import { authAPI, domainsAPI, mailboxesAPI } from './services/api';
|
||||
import { authAPI, domainsAPI, mailboxesAPI, healthAPI } from './services/api';
|
||||
|
||||
function App() {
|
||||
const [user, setUser] = useState(null);
|
||||
@@ -40,6 +42,10 @@ function App() {
|
||||
const [showChangePw, setShowChangePw] = useState(false);
|
||||
const [showDomainQuota, setShowDomainQuota] = useState(false);
|
||||
const [showBilling, setShowBilling] = useState(false);
|
||||
const [showHealth, setShowHealth] = useState(false);
|
||||
|
||||
// Persisted health status for the currently selected domain (drives the banner).
|
||||
const [healthStatus, setHealthStatus] = useState(null);
|
||||
|
||||
const showToast = useCallback((message, type = 'success') => {
|
||||
setToast({ message, type });
|
||||
@@ -60,6 +66,20 @@ function App() {
|
||||
setMailboxes(list);
|
||||
}, []);
|
||||
|
||||
// Load (or re-load) the persisted health status for a domain.
|
||||
// Cheap call — just reads from PostgreSQL, no checks are run.
|
||||
const loadHealthStatus = useCallback(async (domain) => {
|
||||
if (!domain) { setHealthStatus(null); return; }
|
||||
try {
|
||||
const status = await healthAPI.getStatus(domain);
|
||||
setHealthStatus(status); // null if never checked, that's fine
|
||||
} catch (err) {
|
||||
// Silent: don't block the UI just because health status load failed.
|
||||
console.warn('Failed to load health status:', err);
|
||||
setHealthStatus(null);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
@@ -84,6 +104,7 @@ function App() {
|
||||
if (first) {
|
||||
setBusyMessage('Refreshing quotas...');
|
||||
await loadMailboxes(first, true);
|
||||
await loadHealthStatus(first);
|
||||
}
|
||||
} catch (err) {
|
||||
showToast(`Failed to load: ${err.message}`, 'error');
|
||||
@@ -100,6 +121,7 @@ function App() {
|
||||
setBusyMessage('Loading mailboxes...');
|
||||
try {
|
||||
await loadMailboxes(domain, true);
|
||||
await loadHealthStatus(domain);
|
||||
} catch (err) {
|
||||
showToast(`Failed to load mailboxes: ${err.message}`, 'error');
|
||||
} finally {
|
||||
@@ -115,7 +137,10 @@ function App() {
|
||||
if (selectedDomain && !list.find((d) => d.domain === selectedDomain)) {
|
||||
const first = list[0]?.domain || null;
|
||||
setSelectedDomain(first);
|
||||
if (first) await loadMailboxes(first, false);
|
||||
if (first) {
|
||||
await loadMailboxes(first, false);
|
||||
await loadHealthStatus(first);
|
||||
}
|
||||
} else if (selectedDomain) {
|
||||
await loadMailboxes(selectedDomain, false);
|
||||
}
|
||||
@@ -133,6 +158,7 @@ function App() {
|
||||
setDomains([]);
|
||||
setMailboxes([]);
|
||||
setSelectedDomain(null);
|
||||
setHealthStatus(null);
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
@@ -167,6 +193,12 @@ function App() {
|
||||
}
|
||||
};
|
||||
|
||||
// Called by HealthModal after a fresh check completes — re-load the
|
||||
// persisted summary so the banner reflects the new state.
|
||||
const handleHealthChecked = async () => {
|
||||
if (selectedDomain) await loadHealthStatus(selectedDomain);
|
||||
};
|
||||
|
||||
if (!bootChecked) {
|
||||
return <div className="min-h-screen flex items-center justify-center text-gray-400">Loading...</div>;
|
||||
}
|
||||
@@ -269,6 +301,9 @@ function App() {
|
||||
)}
|
||||
|
||||
<section className="card relative">
|
||||
{/* Health banner (shows only when last check found problems) */}
|
||||
<HealthBanner status={healthStatus} onOpen={() => setShowHealth(true)} />
|
||||
|
||||
<div className="flex items-start justify-between gap-4 mb-5 flex-wrap">
|
||||
<div>
|
||||
<h2 className="text-base font-semibold text-gray-900">
|
||||
@@ -278,7 +313,16 @@ function App() {
|
||||
Create/delete mailboxes, reset passwords, edit rules. Quotas are refreshed when you open a domain.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-row flex-nowrap gap-2">
|
||||
<div className="flex flex-row flex-wrap gap-2">
|
||||
<button
|
||||
onClick={() => setShowHealth(true)}
|
||||
disabled={!selectedDomain}
|
||||
className="btn-secondary"
|
||||
title="Run DNS, DMS and TLS cert checks for this domain"
|
||||
>
|
||||
<FiActivity className="w-4 h-4 mr-2" />
|
||||
Check health
|
||||
</button>
|
||||
{isSuperAdmin && (
|
||||
<button
|
||||
onClick={() => setShowDomainQuota(true)}
|
||||
@@ -441,6 +485,13 @@ function App() {
|
||||
onToast={showToast}
|
||||
/>
|
||||
)}
|
||||
<HealthModal
|
||||
open={showHealth}
|
||||
domain={selectedDomain}
|
||||
onClose={() => setShowHealth(false)}
|
||||
onCheckedReport={handleHealthChecked}
|
||||
onToast={showToast}
|
||||
/>
|
||||
<ChangeMyPasswordModal
|
||||
open={showChangePw}
|
||||
onClose={() => setShowChangePw(false)}
|
||||
|
||||
Reference in New Issue
Block a user