@boundless-oss/atlas
Version:
Atlas - MCP Server for comprehensive startup project management
266 lines • 11.6 kB
JavaScript
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