diff --git a/frontend/src/pages/api/god/[...action].ts b/frontend/src/pages/api/god/[...action].ts index 191ae3d..af4e9b6 100644 --- a/frontend/src/pages/api/god/[...action].ts +++ b/frontend/src/pages/api/god/[...action].ts @@ -6,6 +6,7 @@ * * Endpoints: * GET /api/god/health - Full system health check + * GET /api/god/services - Quick service status (all 4 containers) * GET /api/god/db-status - Database connection test * POST /api/god/sql - Execute raw SQL (dangerous!) * GET /api/god/tables - List all tables @@ -14,6 +15,7 @@ import type { APIRoute } from 'astro'; import { Pool } from 'pg'; +import Redis from 'ioredis'; // Direct PostgreSQL connection (bypasses Directus) const pool = new Pool({ @@ -27,6 +29,9 @@ const pool = new Pool({ connectionTimeoutMillis: 5000, }); +// Directus URL +const DIRECTUS_URL = process.env.PUBLIC_DIRECTUS_URL || 'http://directus:8055'; + // God Mode Token validation function validateGodToken(request: Request): boolean { const token = request.headers.get('X-God-Token') || @@ -63,6 +68,8 @@ export const GET: APIRoute = async ({ request, url }) => { switch (action) { case 'health': return await getHealth(); + case 'services': + return await getServices(); case 'db-status': return await getDbStatus(); case 'tables': @@ -72,8 +79,10 @@ export const GET: APIRoute = async ({ request, url }) => { default: return json({ message: '🔱 God Mode Backdoor Active', + frontend: 'RUNNING ✅', endpoints: { 'GET /api/god/health': 'Full system health check', + 'GET /api/god/services': 'Quick status of all 4 containers', 'GET /api/god/db-status': 'Database connection test', 'GET /api/god/tables': 'List all tables', 'GET /api/god/logs': 'Recent work_log entries', @@ -121,14 +130,103 @@ export const POST: APIRoute = async ({ request, url }) => { } }; +// Quick service status check +async function getServices() { + const services: Record = { + timestamp: new Date().toISOString(), + frontend: { status: '✅ RUNNING', note: 'You are seeing this response' } + }; + + // Check PostgreSQL + try { + const start = Date.now(); + await pool.query('SELECT 1'); + services.postgresql = { + status: '✅ RUNNING', + latency_ms: Date.now() - start + }; + } catch (error: any) { + services.postgresql = { + status: '❌ DOWN', + error: error.message + }; + } + + // Check Redis + try { + const redis = new Redis({ + host: process.env.REDIS_HOST || 'redis', + port: 6379, + connectTimeout: 3000, + maxRetriesPerRequest: 1 + }); + const start = Date.now(); + await redis.ping(); + services.redis = { + status: '✅ RUNNING', + latency_ms: Date.now() - start + }; + redis.disconnect(); + } catch (error: any) { + services.redis = { + status: '❌ DOWN', + error: error.message + }; + } + + // Check Directus + try { + const start = Date.now(); + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 5000); + + const response = await fetch(`${DIRECTUS_URL}/server/health`, { + signal: controller.signal + }); + clearTimeout(timeout); + + if (response.ok) { + const data = await response.json(); + services.directus = { + status: '✅ RUNNING', + latency_ms: Date.now() - start, + health: data.status + }; + } else { + services.directus = { + status: '⚠️ UNHEALTHY', + http_status: response.status + }; + } + } catch (error: any) { + services.directus = { + status: '❌ DOWN', + error: error.name === 'AbortError' ? 'Timeout (5s)' : error.message + }; + } + + // Summary + const allUp = services.postgresql.status.includes('✅') && + services.redis.status.includes('✅') && + services.directus.status.includes('✅'); + + services.summary = allUp ? '✅ ALL SERVICES HEALTHY' : '⚠️ SOME SERVICES DOWN'; + + return json(services); +} + // Health check implementation async function getHealth() { const start = Date.now(); const checks: Record = { timestamp: new Date().toISOString(), - uptime: process.uptime(), - memory: process.memoryUsage(), + uptime_seconds: Math.round(process.uptime()), + memory: { + rss_mb: Math.round(process.memoryUsage().rss / 1024 / 1024), + heap_used_mb: Math.round(process.memoryUsage().heapUsed / 1024 / 1024), + heap_total_mb: Math.round(process.memoryUsage().heapTotal / 1024 / 1024), + }, }; // PostgreSQL check @@ -136,24 +234,70 @@ async function getHealth() { const dbStart = Date.now(); const result = await pool.query('SELECT NOW() as time, current_database() as db, current_user as user'); checks.postgresql = { - status: 'healthy', + status: '✅ healthy', latency_ms: Date.now() - dbStart, ...result.rows[0] }; } catch (error: any) { checks.postgresql = { - status: 'unhealthy', + status: '❌ unhealthy', error: error.message }; } // Connection pool status - checks.pool = { + checks.pg_pool = { total: pool.totalCount, idle: pool.idleCount, waiting: pool.waitingCount }; + // Redis check + try { + const redis = new Redis({ + host: process.env.REDIS_HOST || 'redis', + port: 6379, + connectTimeout: 3000, + maxRetriesPerRequest: 1 + }); + const redisStart = Date.now(); + const info = await redis.info('server'); + checks.redis = { + status: '✅ healthy', + latency_ms: Date.now() - redisStart, + version: info.match(/redis_version:([^\r\n]+)/)?.[1] + }; + redis.disconnect(); + } catch (error: any) { + checks.redis = { + status: '❌ unhealthy', + error: error.message + }; + } + + // Directus check + try { + const directusStart = Date.now(); + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 5000); + + const response = await fetch(`${DIRECTUS_URL}/server/health`, { + signal: controller.signal + }); + clearTimeout(timeout); + + checks.directus = { + status: response.ok ? '✅ healthy' : '⚠️ unhealthy', + latency_ms: Date.now() - directusStart, + http_status: response.status + }; + } catch (error: any) { + checks.directus = { + status: '❌ unreachable', + error: error.name === 'AbortError' ? 'Timeout (5s)' : error.message + }; + } + // Directus tables check try { const tables = await pool.query(` @@ -163,12 +307,9 @@ async function getHealth() { AND table_name LIKE 'directus_%' ORDER BY table_name `); - checks.directus = { - system_tables: tables.rows.length, - tables: tables.rows.map(r => r.table_name) - }; + checks.directus_tables = tables.rows.length; } catch (error: any) { - checks.directus = { status: 'error', error: error.message }; + checks.directus_tables = 0; } // Custom tables check @@ -185,7 +326,7 @@ async function getHealth() { tables: tables.rows.map(r => r.table_name) }; } catch (error: any) { - checks.custom_tables = { status: 'error', error: error.message }; + checks.custom_tables = { count: 0, error: error.message }; } checks.total_latency_ms = Date.now() - start;