Add System Health Monitor: RAM/CPU/locks tracking with emergency kill controls + Visual Builder plan
This commit is contained in:
49
src/pages/api/shim/emergency/kill-locks.ts
Normal file
49
src/pages/api/shim/emergency/kill-locks.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
// EMERGENCY API: Kill stuck database locks
|
||||
// USE WITH CAUTION - Terminates blocking queries
|
||||
|
||||
import type { APIRoute } from 'astro';
|
||||
import { killStuckLocks, getBlockingQueries } from '@/lib/shim/health';
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
try {
|
||||
// STRICT token validation - this is destructive
|
||||
const authHeader = request.headers.get('Authorization');
|
||||
const token = authHeader?.replace('Bearer ', '');
|
||||
|
||||
const godToken = import.meta.env.GOD_MODE_TOKEN;
|
||||
if (!godToken || token !== godToken) {
|
||||
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
|
||||
status: 401,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
// Get list of what will be killed before killing
|
||||
const blocking = await getBlockingQueries();
|
||||
|
||||
// Execute kill
|
||||
const killedCount = await killStuckLocks();
|
||||
|
||||
console.warn(`[EMERGENCY] Killed ${killedCount} stuck locks`, { blocking });
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
success: true,
|
||||
killedCount,
|
||||
blockedQueries: blocking.length,
|
||||
message: `Terminated ${killedCount} blocking queries`
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('[EMERGENCY] Kill locks failed:', error);
|
||||
return new Response(JSON.stringify({
|
||||
error: 'Kill locks failed',
|
||||
message: error.message
|
||||
}), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,8 +1,9 @@
|
||||
// API Route: GET /api/shim/health
|
||||
// Returns connection pool stats and database health
|
||||
// Returns connection pool stats, database health, and system metrics (RAM/CPU/locks)
|
||||
|
||||
import type { APIRoute } from 'astro';
|
||||
import { getPoolStats, getDatabaseStats, getVacuumCandidates } from '@/lib/shim/pool';
|
||||
import { getSystemHealth } from '@/lib/shim/health';
|
||||
|
||||
export const GET: APIRoute = async ({ request }) => {
|
||||
try {
|
||||
@@ -18,21 +19,35 @@ export const GET: APIRoute = async ({ request }) => {
|
||||
});
|
||||
}
|
||||
|
||||
// Get health stats
|
||||
// Get all health stats
|
||||
const poolStats = getPoolStats();
|
||||
const dbStats = await getDatabaseStats();
|
||||
const vacuumCandidates = await getVacuumCandidates();
|
||||
const systemHealth = await getSystemHealth();
|
||||
const needsVacuum = vacuumCandidates.length > 0 && vacuumCandidates[0].deadPercent > 20;
|
||||
|
||||
// Overall status (most critical wins)
|
||||
const overallStatus =
|
||||
systemHealth.status === 'critical' || poolStats.status === 'critical'
|
||||
? 'critical'
|
||||
: systemHealth.status === 'warning' || poolStats.status === 'warning'
|
||||
? 'warning'
|
||||
: 'healthy';
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
timestamp: new Date().toISOString(),
|
||||
status: overallStatus,
|
||||
system: systemHealth,
|
||||
pool: poolStats,
|
||||
database: dbStats,
|
||||
vacuum: {
|
||||
recommended: needsVacuum,
|
||||
candidates: vacuumCandidates
|
||||
},
|
||||
status: poolStats.status
|
||||
alerts: [
|
||||
...systemHealth.alerts,
|
||||
...(poolStats.status !== 'healthy' ? [poolStats.message] : [])
|
||||
]
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
|
||||
@@ -6,6 +6,7 @@ import { getPoolStats, getDatabaseStats, getVacuumCandidates } from '@/lib/shim/
|
||||
import { getArticlesCountByStatus } from '@/lib/shim/articles';
|
||||
import { getSitesCountByStatus } from '@/lib/shim/sites';
|
||||
import ShimMonitor from '@/components/shim/ShimMonitor';
|
||||
import HealthDash from '@/components/shim/HealthDash';
|
||||
|
||||
// Server-side stats (instant load)
|
||||
const poolStats = getPoolStats();
|
||||
@@ -182,6 +183,17 @@ const totalSites = Object.values(siteCounts).reduce((a, b) => a + b, 0);
|
||||
</div>
|
||||
)}
|
||||
|
||||
<!-- System Health Monitor (RAM/CPU/Locks) -->
|
||||
<div class="bg-slate-800 rounded-lg border border-slate-700">
|
||||
<div class="p-4 border-b border-slate-700">
|
||||
<h2 class="text-white font-semibold text-lg">🔋 System Health Monitor</h2>
|
||||
<p class="text-slate-400 text-sm mt-1">Real-time RAM, CPU, and database lock monitoring (2s refresh)</p>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<HealthDash client:load />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Two-Column Layout -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
|
||||
@@ -282,6 +294,7 @@ const totalSites = Object.values(siteCounts).reduce((a, b) => a + b, 0);
|
||||
<li>✅ <strong>Zod Validation</strong> - All data validated before SQL execution</li>
|
||||
<li>✅ <strong>SEO Enforcement</strong> - Cannot publish without metadata</li>
|
||||
<li>✅ <strong>Connection Monitoring</strong> - Real-time pool health tracking</li>
|
||||
<li>✅ <strong>System Health Monitor</strong> - RAM/CPU/locks with emergency controls</li>
|
||||
<li>✅ <strong>Auto VACUUM Detection</strong> - Prevents performance degradation</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user