← back to Designer Wallcoverings
DW-Agents/dw-agents/agent-dashboard-monitor/dashboard-monitor.ts
372 lines
/**
* DW-Agents: Dashboard Monitor
*
* Single-page dashboard with iframes showing all agents
* Port: 9893
*/
import express from 'express';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import { requireGlobalAuth } from '../shared-global-auth';
const app = express();
const PORT = process.env.PORT || 9899; // Changed to 9899 with 30-second refresh
app.use(express.json());
app.use(cookieParser());
app.use(session({
secret: process.env.SESSION_SECRET || 'dw-dashboard-monitor-secret-2025',
resave: false,
saveUninitialized: false,
cookie: {
secure: false,
httpOnly: true,
maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days
}
}));
// Apply authentication middleware
app.use(requireGlobalAuth);
// Agent list with ports - Updated with currently running agents only
const AGENTS = [
{ name: 'CEO Dashboard', port: 7120, category: 'Executive' },
{ name: 'VP Operations', port: 7125, category: 'Executive' },
{ name: 'Master Hub', port: 9893, category: 'Admin' },
{ name: 'Control Panel', port: 7200, category: 'Admin' },
{ name: 'Digital Samples', port: 9879, category: 'Product' },
{ name: 'Legal Team', port: 9878, category: 'Operations' },
{ name: 'Purchasing Office', port: 9880, category: 'Operations' },
{ name: 'Shopify Store', port: 7238, category: 'Product' },
{ name: 'Trend Research', port: 9883, category: 'Research' },
{ name: 'Server Uptime', port: 9887, category: 'DevOps' },
{ name: 'Log Monitor', port: 7239, category: 'DevOps' },
{ name: 'Needs Attention', port: 9886, category: 'Operations' },
{ name: 'UI Manager', port: 7240, category: 'Design' },
{ name: 'BigQuery Gov', port: 9876, category: 'Data' },
{ name: 'Crash Loop Resolver', port: 9895, category: 'DevOps' }
];
app.get('/', (req, res) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>DW-Agents Dashboard Monitor</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.header {
background: rgba(255,255,255,0.95);
padding: 20px 30px;
border-radius: 15px;
margin-bottom: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
}
.header h1 {
color: #667eea;
font-size: 2em;
display: flex;
align-items: center;
gap: 15px;
}
.controls {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.filter-group {
display: flex;
gap: 10px;
flex-wrap: wrap;
background: rgba(255,255,255,0.95);
padding: 15px;
border-radius: 10px;
margin-bottom: 20px;
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
}
.filter-btn {
background: #f0f0f0;
color: #333;
border: 2px solid transparent;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
transition: all 0.2s;
}
.filter-btn:hover {
background: #e0e0e0;
}
.filter-btn.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-color: #667eea;
}
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(600px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.agent-frame {
background: white;
border-radius: 15px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
transition: all 0.3s;
}
.agent-frame:hover {
transform: translateY(-5px);
box-shadow: 0 15px 40px rgba(0,0,0,0.3);
}
.agent-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.agent-name {
font-weight: bold;
font-size: 1.1em;
}
.agent-port {
background: rgba(255,255,255,0.2);
padding: 4px 12px;
border-radius: 12px;
font-size: 0.9em;
font-family: monospace;
}
.agent-category {
background: rgba(255,255,255,0.3);
padding: 4px 12px;
border-radius: 12px;
font-size: 0.85em;
margin-left: 10px;
}
iframe {
width: 100%;
height: 600px;
border: none;
display: block;
}
.status-indicator {
width: 10px;
height: 10px;
border-radius: 50%;
display: inline-block;
margin-right: 8px;
animation: pulse 2s infinite;
}
.status-online {
background: #10b981;
box-shadow: 0 0 10px #10b981;
}
.status-offline {
background: #ef4444;
box-shadow: 0 0 10px #ef4444;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.reload-btn {
background: rgba(255,255,255,0.2);
border: 1px solid rgba(255,255,255,0.3);
color: white;
padding: 6px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 0.9em;
transition: all 0.2s;
}
.reload-btn:hover {
background: rgba(255,255,255,0.3);
}
@media (max-width: 1400px) {
.grid-container {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="header">
<h1>
<span>🎛️</span>
<span>DW-Agents Dashboard Monitor</span>
</h1>
<div class="controls">
<button class="btn" onclick="reloadAll()">🔄 Reload All</button>
<button class="btn" onclick="toggleSize()">📏 Toggle Size</button>
<button class="btn" onclick="checkStatus()">✅ Check Status</button>
</div>
</div>
<div class="filter-group">
<span style="font-weight: 600; color: #333; margin-right: 10px;">Filter by Category:</span>
<button class="filter-btn active" onclick="filterCategory('all')">All</button>
<button class="filter-btn" onclick="filterCategory('Executive')">Executive</button>
<button class="filter-btn" onclick="filterCategory('Operations')">Operations</button>
<button class="filter-btn" onclick="filterCategory('Product')">Product</button>
<button class="filter-btn" onclick="filterCategory('Marketing')">Marketing</button>
<button class="filter-btn" onclick="filterCategory('Finance')">Finance</button>
<button class="filter-btn" onclick="filterCategory('DevOps')">DevOps</button>
<button class="filter-btn" onclick="filterCategory('Data')">Data</button>
<button class="filter-btn" onclick="filterCategory('Research')">Research</button>
<button class="filter-btn" onclick="filterCategory('Design')">Design</button>
<button class="filter-btn" onclick="filterCategory('Support')">Support</button>
<button class="filter-btn" onclick="filterCategory('Admin')">Admin</button>
</div>
<div class="grid-container" id="agentGrid">
${AGENTS.map(agent => `
<div class="agent-frame" data-category="${agent.category}">
<div class="agent-header">
<div>
<span class="status-indicator status-online" id="status-${agent.port}"></span>
<span class="agent-name">${agent.name}</span>
<span class="agent-category">${agent.category}</span>
</div>
<div>
<span class="agent-port">:${agent.port}</span>
<button class="reload-btn" onclick="reloadFrame(${agent.port})">🔄</button>
<button class="reload-btn" onclick="openInNew(${agent.port})">🔗</button>
</div>
</div>
<iframe
id="frame-${agent.port}"
src="http://45.61.58.125:${agent.port}/"
loading="lazy"
sandbox="allow-same-origin allow-scripts allow-forms allow-popups"
></iframe>
</div>
`).join('')}
</div>
<script>
let currentSize = 600;
function reloadAll() {
document.querySelectorAll('iframe').forEach(frame => {
frame.src = frame.src;
});
}
function reloadFrame(port) {
const frame = document.getElementById('frame-' + port);
frame.src = frame.src;
}
function openInNew(port) {
window.open('http://45.61.58.125:' + port + '/', '_blank');
}
function toggleSize() {
currentSize = currentSize === 600 ? 400 : currentSize === 400 ? 800 : 600;
document.querySelectorAll('iframe').forEach(frame => {
frame.style.height = currentSize + 'px';
});
}
function filterCategory(category) {
// Update active button
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.classList.remove('active');
});
event.target.classList.add('active');
// Filter frames
document.querySelectorAll('.agent-frame').forEach(frame => {
if (category === 'all' || frame.dataset.category === category) {
frame.style.display = 'block';
} else {
frame.style.display = 'none';
}
});
}
async function checkStatus() {
const ports = ${JSON.stringify(AGENTS.map(a => a.port))};
for (const port of ports) {
try {
const response = await fetch('http://45.61.58.125:' + port + '/', {
method: 'HEAD',
mode: 'no-cors'
});
const indicator = document.getElementById('status-' + port);
indicator.className = 'status-indicator status-online';
} catch (error) {
const indicator = document.getElementById('status-' + port);
indicator.className = 'status-indicator status-offline';
}
}
}
// Check status on load
setTimeout(checkStatus, 1000);
// Auto-refresh status every 30 seconds
setInterval(checkStatus, 30000);
// Auto-refresh the entire page every 30 seconds
setInterval(() => {
location.reload();
}, 30000);
</script>
</body>
</html>
`);
});
// Health check endpoint
app.get('/api/health', (req, res) => {
res.json({
status: 'online',
port: PORT,
agentCount: AGENTS.length,
uptime: process.uptime()
});
});
app.listen(PORT, '0.0.0.0', () => {
console.log('');
console.log('🎛️ Dashboard Monitor');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('🌍 External: http://45.61.58.125:' + PORT);
console.log('🏠 Local: http://localhost:' + PORT);
console.log('');
console.log('📊 Monitoring ' + AGENTS.length + ' agents');
console.log('✅ Dashboard ready...');
console.log('');
});