feat: show commit hash in footer and implement auto server health check for AppWPP
This commit is contained in:
+7
-1
@@ -8,7 +8,8 @@ import {
|
|||||||
Lock,
|
Lock,
|
||||||
Loader2,
|
Loader2,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
Bookmark
|
Bookmark,
|
||||||
|
GitCommit
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import Header from './components/Header';
|
import Header from './components/Header';
|
||||||
import SecurityBadge from './components/SecurityBadge';
|
import SecurityBadge from './components/SecurityBadge';
|
||||||
@@ -251,6 +252,11 @@ export default function App() {
|
|||||||
<span className="footer-shield">
|
<span className="footer-shield">
|
||||||
<ShieldCheck size={14} /> 100% no cliente (Executa em qualquer navegador)
|
<ShieldCheck size={14} /> 100% no cliente (Executa em qualquer navegador)
|
||||||
</span>
|
</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span className="footer-commit" title="Versão do commit da aplicação">
|
||||||
|
<GitCommit size={14} className="text-cyan" />
|
||||||
|
<span>Commit: <code className="commit-hash-pill">{typeof __COMMIT_HASH__ !== 'undefined' ? __COMMIT_HASH__ : 'dev'}</code></span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import {
|
|||||||
AlertCircle,
|
AlertCircle,
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
Info,
|
Info,
|
||||||
Edit2
|
Edit2,
|
||||||
|
RefreshCw
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { getStoredServers, saveServers } from '../../utils/appWppStorage';
|
import { getStoredServers, saveServers } from '../../utils/appWppStorage';
|
||||||
import {
|
import {
|
||||||
@@ -24,7 +25,9 @@ import {
|
|||||||
checkSessionStatus,
|
checkSessionStatus,
|
||||||
clearSessionData,
|
clearSessionData,
|
||||||
generateToken,
|
generateToken,
|
||||||
ensureQRPrefix
|
ensureQRPrefix,
|
||||||
|
testServerConnection,
|
||||||
|
listAllServerSessions
|
||||||
} from '../../utils/wppConnectApi';
|
} from '../../utils/wppConnectApi';
|
||||||
import WppSessionCard from './WppSessionCard';
|
import WppSessionCard from './WppSessionCard';
|
||||||
import ModalServer from './ModalServer';
|
import ModalServer from './ModalServer';
|
||||||
@@ -91,6 +94,63 @@ export default function AppWPP() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const activeServer = servers.find((s) => s.id === activeServerId) || null;
|
const activeServer = servers.find((s) => s.id === activeServerId) || null;
|
||||||
|
const [isCheckingServer, setIsCheckingServer] = useState(false);
|
||||||
|
|
||||||
|
const checkServerOnlineStatus = async (serverToCheck) => {
|
||||||
|
const srv = serverToCheck || servers.find((s) => s.id === activeServerId) || activeServer;
|
||||||
|
if (!srv || !srv.baseUrl || !srv.secretKey) return;
|
||||||
|
|
||||||
|
setIsCheckingServer(true);
|
||||||
|
try {
|
||||||
|
const res = await testServerConnection(srv.baseUrl, srv.secretKey);
|
||||||
|
const isOnline = res.ok === true;
|
||||||
|
updateServersState((prevServers) =>
|
||||||
|
prevServers.map((s) => (s.id === srv.id ? { ...s, online: isOnline } : s))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Se estiver online, sincroniza o status das sessões já ativas no servidor
|
||||||
|
if (isOnline) {
|
||||||
|
try {
|
||||||
|
const sessionsOnServer = await listAllServerSessions(srv);
|
||||||
|
if (Array.isArray(sessionsOnServer)) {
|
||||||
|
updateServersState((prevServers) =>
|
||||||
|
prevServers.map((s) => {
|
||||||
|
if (s.id !== srv.id) return s;
|
||||||
|
const updatedSessions = (s.sessions || []).map((sess) => {
|
||||||
|
const match = sessionsOnServer.find((x) =>
|
||||||
|
x === sess.name || x?.name === sess.name || x?.session === sess.name
|
||||||
|
);
|
||||||
|
if (match) {
|
||||||
|
const statusStr = typeof match === 'string' ? 'connected' : (match.status || 'connected');
|
||||||
|
const isConn = statusStr === 'CONNECTED' || statusStr === 'connected' || statusStr === true;
|
||||||
|
return { ...sess, status: isConn ? 'connected' : sess.status };
|
||||||
|
}
|
||||||
|
return sess;
|
||||||
|
});
|
||||||
|
return { ...s, sessions: updatedSessions };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
updateServersState((prevServers) =>
|
||||||
|
prevServers.map((s) => (s.id === srv.id ? { ...s, online: false } : s))
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
setIsCheckingServer(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checa automaticamente o status do servidor ativo na inicialização e ao alternar
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeServerId && servers.length > 0) {
|
||||||
|
const srv = servers.find((s) => s.id === activeServerId);
|
||||||
|
if (srv) {
|
||||||
|
checkServerOnlineStatus(srv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [activeServerId, servers.length]);
|
||||||
|
|
||||||
// Servidor CRUD
|
// Servidor CRUD
|
||||||
const handleSaveServer = (serverData) => {
|
const handleSaveServer = (serverData) => {
|
||||||
@@ -415,9 +475,20 @@ export default function AppWPP() {
|
|||||||
<div className="wpp-server-title-box">
|
<div className="wpp-server-title-box">
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
||||||
<h2 className="wpp-view-server-name">{activeServer.name}</h2>
|
<h2 className="wpp-view-server-name">{activeServer.name}</h2>
|
||||||
<span className={`wpp-badge-status ${activeServer.online ? 'badge-online' : 'badge-offline'}`}>
|
<div className="wpp-status-wrapper">
|
||||||
{activeServer.online ? 'Online' : 'Offline'}
|
<span className={`wpp-badge-status ${isCheckingServer ? 'badge-checking' : (activeServer.online ? 'badge-online' : 'badge-offline')}`}>
|
||||||
</span>
|
{isCheckingServer ? 'Verificando...' : (activeServer.online ? 'Online' : 'Offline')}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn-refresh-status"
|
||||||
|
title="Verificar status do servidor agora"
|
||||||
|
onClick={() => checkServerOnlineStatus(activeServer)}
|
||||||
|
disabled={isCheckingServer}
|
||||||
|
>
|
||||||
|
<RefreshCw size={12} className={isCheckingServer ? 'animate-spin' : ''} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="wpp-view-server-url">{activeServer.baseUrl}</span>
|
<span className="wpp-view-server-url">{activeServer.baseUrl}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1262,6 +1262,24 @@ body {
|
|||||||
color: #34d399;
|
color: #34d399;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footer-commit {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.commit-hash-pill {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
background: rgba(14, 165, 233, 0.12);
|
||||||
|
border: 1px solid rgba(14, 165, 233, 0.25);
|
||||||
|
color: #38bdf8;
|
||||||
|
padding: 0.1rem 0.45rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
/* Utilities */
|
/* Utilities */
|
||||||
.text-cyan { color: var(--accent-cyan); }
|
.text-cyan { color: var(--accent-cyan); }
|
||||||
.text-emerald { color: var(--accent-emerald); }
|
.text-emerald { color: var(--accent-emerald); }
|
||||||
@@ -1640,6 +1658,42 @@ body {
|
|||||||
border: 1px solid rgba(100, 116, 139, 0.3);
|
border: 1px solid rgba(100, 116, 139, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.badge-checking {
|
||||||
|
background: rgba(234, 179, 8, 0.15);
|
||||||
|
color: #facc15;
|
||||||
|
border: 1px solid rgba(234, 179, 8, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wpp-status-wrapper {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-refresh-status {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
color: var(--text-muted);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0.2rem 0.35rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-refresh-status:hover:not(:disabled) {
|
||||||
|
color: #38bdf8;
|
||||||
|
border-color: rgba(56, 189, 248, 0.4);
|
||||||
|
background: rgba(56, 189, 248, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-refresh-status:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.wpp-server-actions-group {
|
.wpp-server-actions-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
+17
-1
@@ -1,9 +1,25 @@
|
|||||||
import react from '@vitejs/plugin-react'
|
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
import { execSync } from 'child_process'
|
||||||
|
|
||||||
|
let commitHash = process.env.COMMIT_REF
|
||||||
|
? process.env.COMMIT_REF.slice(0, 7)
|
||||||
|
: '';
|
||||||
|
|
||||||
|
if (!commitHash) {
|
||||||
|
try {
|
||||||
|
commitHash = execSync('git rev-parse --short HEAD').toString().trim();
|
||||||
|
} catch (e) {
|
||||||
|
commitHash = 'dev';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
|
define: {
|
||||||
|
__COMMIT_HASH__: JSON.stringify(commitHash)
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api-wpp': {
|
'/api-wpp': {
|
||||||
|
|||||||
Reference in New Issue
Block a user