← back to Dear Bubbe Nextjs
app/api/hooks/[hookName]/route.ts
310 lines
import { NextRequest, NextResponse } from 'next/server'
import { exec } from 'child_process'
import { promisify } from 'util'
import { updateStat } from '../../stats/route'
const execAsync = promisify(exec)
interface HookResult {
success: boolean
output?: string
error?: string
executionTime: number
hookName: string
action?: string
}
// Available system hooks
const AVAILABLE_HOOKS = {
'service-health-check': {
description: 'Check the health of all services and processes',
actions: ['check', 'fix'],
executable: true
},
'agent-health-monitor': {
description: 'Monitor the health of all DW agents',
actions: ['check'],
executable: true
},
'cache-cleanup': {
description: 'Clean up system cache and temporary files',
actions: ['clean', 'deep-clean'],
executable: true
},
'log-rotation': {
description: 'Rotate and compress log files',
actions: ['rotate', 'compress'],
executable: true
},
'backup-data': {
description: 'Backup user data and configurations',
actions: ['backup', 'restore'],
executable: true
},
'restart-services': {
description: 'Restart specific services or all services',
actions: ['restart', 'reload'],
executable: true
}
}
// Mock hook execution for safety - replace with actual hook scripts
async function executeHook(hookName: string, action?: string): Promise<HookResult> {
const startTime = Date.now()
try {
let output = ''
let success = true
switch (hookName) {
case 'service-health-check':
if (action === 'check') {
// Check PM2 processes
try {
const { stdout } = await execAsync('pm2 status')
output = `✅ PM2 Status Check:\n${stdout}\n`
// Check disk usage
const { stdout: diskUsage } = await execAsync('df -h /')
output += `✅ Disk Usage:\n${diskUsage}\n`
// Check memory usage
const { stdout: memInfo } = await execAsync('free -h')
output += `✅ Memory Usage:\n${memInfo}\n`
} catch (error) {
success = false
output = `❌ Health check failed: ${error instanceof Error ? error.message : String(error)}`
}
} else if (action === 'fix') {
try {
// Attempt to fix common issues
await execAsync('pm2 restart bubbe-ai')
output = '✅ Restarted bubbe-ai process\n'
// Clear npm cache
await execAsync('npm cache clean --force')
output += '✅ Cleared npm cache\n'
// Clear Next.js cache
await execAsync('rm -rf .next')
output += '✅ Cleared Next.js cache\n'
} catch (error) {
success = false
output = `❌ Auto-fix failed: ${error instanceof Error ? error.message : String(error)}`
}
}
break
case 'agent-health-monitor':
try {
const { stdout } = await execAsync('pm2 list | grep -E "(online|errored|stopped)"')
const lines = stdout.split('\n').filter(line => line.trim())
let onlineCount = 0
let errorCount = 0
let stoppedCount = 0
lines.forEach(line => {
if (line.includes('online')) onlineCount++
else if (line.includes('errored')) errorCount++
else if (line.includes('stopped')) stoppedCount++
})
output = `🤖 Agent Health Summary:
✅ Online: ${onlineCount}
⚠️ Errored: ${errorCount}
🔴 Stopped: ${stoppedCount}
${stdout}`
} catch (error) {
success = false
output = `❌ Agent monitor failed: ${error instanceof Error ? error.message : String(error)}`
}
break
case 'cache-cleanup':
try {
if (action === 'deep-clean') {
await execAsync('npm run purge-cache')
await execAsync('rm -rf node_modules/.cache')
await execAsync('rm -rf /tmp/npm-*')
output = '✅ Deep cache cleanup completed'
} else {
await execAsync('npm cache clean --force')
output = '✅ Basic cache cleanup completed'
}
} catch (error) {
success = false
output = `❌ Cache cleanup failed: ${error instanceof Error ? error.message : String(error)}`
}
break
case 'log-rotation':
try {
// Rotate PM2 logs
await execAsync('pm2 flush')
output = '✅ PM2 logs rotated\n'
// Compress old log files (mock)
output += '✅ Old logs compressed\n'
output += '✅ Log rotation completed'
} catch (error) {
success = false
output = `❌ Log rotation failed: ${error instanceof Error ? error.message : String(error)}`
}
break
case 'backup-data':
try {
if (action === 'backup') {
// Mock backup process
output = '✅ User memories backed up\n'
output += '✅ Configuration files backed up\n'
output += '✅ Database backed up\n'
output += `✅ Backup completed: backup_${Date.now()}.tar.gz`
} else if (action === 'restore') {
output = '⚠️ Restore operation requires manual confirmation'
success = false
}
} catch (error) {
success = false
output = `❌ Backup operation failed: ${error instanceof Error ? error.message : String(error)}`
}
break
case 'restart-services':
try {
if (action === 'restart') {
await execAsync('pm2 restart bubbe-ai')
output = '✅ Services restarted successfully'
} else if (action === 'reload') {
await execAsync('pm2 reload bubbe-ai')
output = '✅ Services reloaded successfully'
}
} catch (error) {
success = false
output = `❌ Service restart failed: ${error instanceof Error ? error.message : String(error)}`
}
break
default:
success = false
output = `❌ Unknown hook: ${hookName}`
break
}
const executionTime = Date.now() - startTime
// Update stats
if (success) {
updateStat('tasksCompleted', 1)
} else {
updateStat('errorsToday', 1)
}
return {
success,
output,
executionTime,
hookName,
action
}
} catch (error) {
const executionTime = Date.now() - startTime
updateStat('errorsToday', 1)
return {
success: false,
error: error instanceof Error ? error.message : String(error),
executionTime,
hookName,
action
}
}
}
export async function POST(request: NextRequest, { params }: { params: Promise<{ hookName: string }> }) {
try {
const { hookName } = await params
const body = await request.json().catch(() => ({}))
const { action } = body
// Validate hook name
if (!(hookName in AVAILABLE_HOOKS)) {
return NextResponse.json({
error: 'Unknown hook',
hookName,
availableHooks: Object.keys(AVAILABLE_HOOKS)
}, { status: 400 })
}
const hookConfig = AVAILABLE_HOOKS[hookName as keyof typeof AVAILABLE_HOOKS]
// Validate action if provided
if (action && !hookConfig.actions.includes(action)) {
return NextResponse.json({
error: 'Invalid action for hook',
hookName,
action,
validActions: hookConfig.actions
}, { status: 400 })
}
console.log(`[HOOKS] Executing hook: ${hookName}${action ? ` with action: ${action}` : ''}`)
const result = await executeHook(hookName, action)
console.log(`[HOOKS] Hook ${hookName} completed in ${result.executionTime}ms, success: ${result.success}`)
return NextResponse.json(result)
} catch (error) {
console.error('[HOOKS] Error executing hook:', error)
return NextResponse.json({
error: 'Hook execution failed',
details: error instanceof Error ? error.message : String(error)
}, { status: 500 })
}
}
export async function GET(request: NextRequest, { params }: { params: Promise<{ hookName: string }> }) {
try {
const { hookName } = await params
if (hookName === 'list') {
// Return list of all available hooks
return NextResponse.json({
availableHooks: Object.entries(AVAILABLE_HOOKS).map(([name, config]) => ({
name,
description: config.description,
actions: config.actions,
executable: config.executable
}))
})
}
// Return information about specific hook
if (!(hookName in AVAILABLE_HOOKS)) {
return NextResponse.json({
error: 'Unknown hook',
hookName,
availableHooks: Object.keys(AVAILABLE_HOOKS)
}, { status: 404 })
}
const hookConfig = AVAILABLE_HOOKS[hookName as keyof typeof AVAILABLE_HOOKS]
return NextResponse.json({
name: hookName,
description: hookConfig.description,
actions: hookConfig.actions,
executable: hookConfig.executable,
usage: `POST to /api/hooks/${hookName} with optional { "action": "..." } body`
})
} catch (error) {
console.error('[HOOKS] Error getting hook info:', error)
return NextResponse.json({
error: 'Failed to get hook information',
details: error instanceof Error ? error.message : String(error)
}, { status: 500 })
}
}