UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

266 lines 11.6 kB
import { ensureDatabaseReady } from '../../storage/sqlite-manager.js'; export function setupApprovalsAPI(app) { // Get all approval requests with filtering app.get('/api/approvals', async (req, res) => { try { const { status, urgency, action, limit = 50, offset = 0 } = req.query; let sql = 'SELECT * FROM approval_requests WHERE 1=1'; const params = []; if (status) { sql += ' AND status = ?'; params.push(status); } if (urgency) { sql += ' AND urgency = ?'; params.push(urgency); } if (action) { sql += ' AND action LIKE ?'; params.push(`%${action}%`); } sql += ' ORDER BY requested_at DESC LIMIT ? OFFSET ?'; params.push(Number(limit), Number(offset)); const db = await ensureDatabaseReady(); const result = await db.query(sql, params); if (!result.success) { throw new Error(result.error || 'Database query failed'); } const approvals = (result.data || []).map((approval) => { let context = {}; try { context = approval.context ? JSON.parse(approval.context) : {}; } catch (e) { console.warn('Failed to parse approval context:', approval.id); } return { id: approval.id, action: approval.action, context, description: approval.description, urgency: approval.urgency, status: approval.status, requestedBy: approval.requested_by, requestedAt: approval.requested_at ? new Date(Number(approval.requested_at)).toISOString() : new Date().toISOString(), resolvedBy: approval.resolved_by, resolvedAt: approval.resolved_at ? new Date(Number(approval.resolved_at)).toISOString() : null, timeoutAt: approval.timeout_at ? new Date(Number(approval.timeout_at)).toISOString() : null, responseData: approval.response_data ? JSON.parse(approval.response_data) : null, notes: approval.notes }; }); res.json({ success: true, data: { approvals }, count: approvals.length, timestamp: new Date().toISOString() }); } catch (error) { console.error('📋 Error fetching approvals:', error); res.status(500).json({ success: false, error: 'Failed to fetch approval requests', message: error.message }); } }); // Get approval statistics app.get('/api/approvals/stats', async (req, res) => { try { const { timeframe = '7d' } = req.query; // Calculate time threshold const now = Date.now(); let timeThreshold = now; switch (timeframe) { case '24h': timeThreshold = now - (24 * 60 * 60 * 1000); break; case '7d': timeThreshold = now - (7 * 24 * 60 * 60 * 1000); break; case '30d': timeThreshold = now - (30 * 24 * 60 * 60 * 1000); break; default: timeThreshold = now - (7 * 24 * 60 * 60 * 1000); } const db = await ensureDatabaseReady(); // Get total counts const totalResult = await db.get('SELECT COUNT(*) as total FROM approval_requests WHERE requested_at > ?', [timeThreshold]); const pendingResult = await db.get('SELECT COUNT(*) as pending FROM approval_requests WHERE status = ? AND requested_at > ?', ['pending', timeThreshold]); const approvedResult = await db.get('SELECT COUNT(*) as approved FROM approval_requests WHERE status = ? AND requested_at > ?', ['approved', timeThreshold]); const rejectedResult = await db.get('SELECT COUNT(*) as rejected FROM approval_requests WHERE status = ? AND requested_at > ?', ['rejected', timeThreshold]); const expiredResult = await db.get('SELECT COUNT(*) as expired FROM approval_requests WHERE status = ? AND requested_at > ?', ['expired', timeThreshold]); // Get urgency breakdown const urgencyResult = await db.query('SELECT urgency, COUNT(*) as count FROM approval_requests WHERE requested_at > ? GROUP BY urgency', [timeThreshold]); const urgencyBreakdown = {}; if (urgencyResult.success && urgencyResult.data) { urgencyResult.data.forEach((row) => { urgencyBreakdown[row.urgency] = row.count; }); } const stats = { total: totalResult.data?.total || 0, pending: pendingResult.data?.pending || 0, approved: approvedResult.data?.approved || 0, rejected: rejectedResult.data?.rejected || 0, expired: expiredResult.data?.expired || 0, urgencyBreakdown, timeframe }; res.json({ success: true, data: stats, timestamp: new Date().toISOString() }); } catch (error) { console.error('📋 Error fetching approval stats:', error); res.status(500).json({ success: false, error: 'Failed to fetch approval statistics', message: error.message }); } }); // Health check for approvals service app.get('/api/approvals/health', async (req, res) => { try { // Test database connection const db = await ensureDatabaseReady(); const testResult = await db.get('SELECT COUNT(*) as count FROM approval_requests LIMIT 1'); if (!testResult.success) { throw new Error('Database connection failed'); } res.json({ success: true, status: 'healthy', data: { databaseConnected: true, approvalSystemActive: true }, timestamp: new Date().toISOString() }); } catch (error) { console.error('📋 Approvals health check failed:', error); res.status(503).json({ success: false, status: 'unhealthy', error: 'Approval system is not functioning properly', message: error.message, timestamp: new Date().toISOString() }); } }); // Get specific approval request app.get('/api/approvals/:id', async (req, res) => { try { const { id } = req.params; const db = await ensureDatabaseReady(); const result = await db.get('SELECT * FROM approval_requests WHERE id = ?', [id]); if (!result.success) { throw new Error(result.error || 'Database query failed'); } if (!result.data) { return res.status(404).json({ success: false, error: 'Approval request not found', approvalId: id }); } const approval = result.data; let context = {}; try { context = approval.context ? JSON.parse(approval.context) : {}; } catch (e) { console.warn('Failed to parse approval context:', approval.id); } const approvalData = { id: approval.id, action: approval.action, context, description: approval.description, urgency: approval.urgency, status: approval.status, requestedBy: approval.requested_by, requestedAt: approval.requested_at ? new Date(Number(approval.requested_at)).toISOString() : new Date().toISOString(), resolvedBy: approval.resolved_by, resolvedAt: approval.resolved_at ? new Date(Number(approval.resolved_at)).toISOString() : null, timeoutAt: approval.timeout_at ? new Date(Number(approval.timeout_at)).toISOString() : null, responseData: approval.response_data ? JSON.parse(approval.response_data) : null, notes: approval.notes }; res.json({ success: true, data: approvalData, timestamp: new Date().toISOString() }); } catch (error) { console.error(`📋 Error fetching approval ${req.params.id}:`, error); res.status(500).json({ success: false, error: 'Failed to fetch approval request', message: error.message }); } }); // Approve or reject an approval request app.post('/api/approvals/:id/respond', async (req, res) => { try { const { id } = req.params; const { decision, notes, responseData, resolvedBy } = req.body; if (!decision || !['approved', 'rejected'].includes(decision)) { return res.status(400).json({ success: false, error: 'Invalid decision. Must be "approved" or "rejected"' }); } const now = Date.now(); const db = await ensureDatabaseReady(); const result = await db.run(`UPDATE approval_requests SET status = ?, resolved_by = ?, resolved_at = ?, notes = ?, response_data = ? WHERE id = ? AND status = 'pending'`, [ decision, resolvedBy || 'dashboard-user', now, notes || null, responseData ? JSON.stringify(responseData) : null, id ]); if (!result.success) { throw new Error(result.error || 'Database update failed'); } if (result.data?.changes === 0) { return res.status(404).json({ success: false, error: 'Approval request not found or already resolved' }); } res.json({ success: true, message: `Approval request ${decision}`, data: { approvalId: id, decision, resolvedBy: resolvedBy || 'dashboard-user', resolvedAt: new Date(now).toISOString(), notes }, timestamp: new Date().toISOString() }); } catch (error) { console.error(`📋 Error responding to approval ${req.params.id}:`, error); res.status(500).json({ success: false, error: 'Failed to respond to approval request', message: error.message }); } }); } //# sourceMappingURL=approvals.js.map