initial commit
This commit is contained in:
187
app/api/admin/reports/route.ts
Normal file
187
app/api/admin/reports/route.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { withAdminAuth } from '@/lib/admin-middleware'
|
||||
import { connectDB } from '@/lib/mongodb'
|
||||
import { User } from '@/models/user'
|
||||
import { Transaction } from '@/models/transaction'
|
||||
import { Billing } from '@/models/billing'
|
||||
import { DeveloperRequest } from '@/models/developer-request'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
return withAdminAuth(request, async (req, admin) => {
|
||||
try {
|
||||
await connectDB()
|
||||
|
||||
const { searchParams } = new URL(request.url)
|
||||
const reportType = searchParams.get('type')
|
||||
const format = searchParams.get('format') || 'json'
|
||||
const dateFrom = searchParams.get('dateFrom')
|
||||
const dateTo = searchParams.get('dateTo')
|
||||
|
||||
// Build date filter
|
||||
const dateFilter: any = {}
|
||||
if (dateFrom || dateTo) {
|
||||
dateFilter.createdAt = {}
|
||||
if (dateFrom) dateFilter.createdAt.$gte = new Date(dateFrom)
|
||||
if (dateTo) dateFilter.createdAt.$lte = new Date(dateTo)
|
||||
}
|
||||
|
||||
let data: any = {}
|
||||
|
||||
switch (reportType) {
|
||||
case 'users':
|
||||
data = await User.find(dateFilter)
|
||||
.select('-password -refreshToken')
|
||||
.sort({ createdAt: -1 })
|
||||
break
|
||||
|
||||
case 'transactions':
|
||||
data = await Transaction.find(dateFilter)
|
||||
.populate('userId', 'name email siliconId')
|
||||
.sort({ createdAt: -1 })
|
||||
break
|
||||
|
||||
case 'billing':
|
||||
data = await Billing.find(
|
||||
dateFilter.createdAt ? { created_at: dateFilter.createdAt } : {}
|
||||
).sort({ created_at: -1 })
|
||||
break
|
||||
|
||||
case 'developer-requests':
|
||||
const devFilter = dateFilter.createdAt ? { createdAt: dateFilter.createdAt } : {}
|
||||
data = await (DeveloperRequest as any)
|
||||
.find(devFilter)
|
||||
.populate('userId', 'name email siliconId')
|
||||
.sort({ createdAt: -1 })
|
||||
break
|
||||
|
||||
case 'summary':
|
||||
// Generate comprehensive summary report
|
||||
const [users, transactions, billings, developerRequests] = await Promise.all([
|
||||
User.find(dateFilter).select('-password -refreshToken'),
|
||||
Transaction.find(dateFilter).populate('userId', 'name email siliconId'),
|
||||
Billing.find(dateFilter.createdAt ? { created_at: dateFilter.createdAt } : {}),
|
||||
(DeveloperRequest as any)
|
||||
.find(dateFilter.createdAt ? { createdAt: dateFilter.createdAt } : {})
|
||||
.populate('userId', 'name email siliconId'),
|
||||
])
|
||||
|
||||
// Calculate summary statistics
|
||||
const userStats = {
|
||||
total: users.length,
|
||||
verified: users.filter((u) => u.isVerified).length,
|
||||
admins: users.filter((u) => u.role === 'admin').length,
|
||||
totalBalance: users.reduce((sum, u) => sum + (u.balance || 0), 0),
|
||||
}
|
||||
|
||||
const transactionStats = {
|
||||
total: transactions.length,
|
||||
totalAmount: transactions.reduce((sum, t) => sum + t.amount, 0),
|
||||
credits: transactions.filter((t) => t.type === 'credit').length,
|
||||
debits: transactions.filter((t) => t.type === 'debit').length,
|
||||
}
|
||||
|
||||
const billingStats = {
|
||||
total: billings.length,
|
||||
totalRevenue: billings.reduce((sum, b) => sum + b.amount, 0),
|
||||
completed: billings.filter((b) => b.payment_status === 'paid').length,
|
||||
pending: billings.filter((b) => b.payment_status === 'pending').length,
|
||||
}
|
||||
|
||||
const developerStats = {
|
||||
total: developerRequests.length,
|
||||
pending: developerRequests.filter((d) => d.status === 'pending').length,
|
||||
inProgress: developerRequests.filter((d) => d.status === 'in_progress').length,
|
||||
completed: developerRequests.filter((d) => d.status === 'completed').length,
|
||||
}
|
||||
|
||||
data = {
|
||||
summary: {
|
||||
users: userStats,
|
||||
transactions: transactionStats,
|
||||
billing: billingStats,
|
||||
developerRequests: developerStats,
|
||||
},
|
||||
users: users.slice(0, 100), // Limit for performance
|
||||
transactions: transactions.slice(0, 100),
|
||||
billings: billings.slice(0, 100),
|
||||
developerRequests: developerRequests.slice(0, 100),
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
return NextResponse.json({ error: 'Invalid report type' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (format === 'csv') {
|
||||
// Convert to CSV format
|
||||
const csv = convertToCSV(data, reportType)
|
||||
|
||||
return new NextResponse(csv, {
|
||||
headers: {
|
||||
'Content-Type': 'text/csv',
|
||||
'Content-Disposition': `attachment; filename="${reportType}_report_${new Date().toISOString().split('T')[0]}.csv"`,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
reportType,
|
||||
generatedAt: new Date().toISOString(),
|
||||
generatedBy: admin.name,
|
||||
dateRange: { from: dateFrom, to: dateTo },
|
||||
data,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Admin reports error:', error)
|
||||
return NextResponse.json({ error: 'Failed to generate report' }, { status: 500 })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function convertToCSV(data: any, reportType: string): string {
|
||||
if (!Array.isArray(data)) {
|
||||
if (reportType === 'summary' && data.summary) {
|
||||
// For summary reports, create a simple CSV with key metrics
|
||||
const lines = [
|
||||
'Metric,Value',
|
||||
`Total Users,${data.summary.users.total}`,
|
||||
`Verified Users,${data.summary.users.verified}`,
|
||||
`Admin Users,${data.summary.users.admins}`,
|
||||
`Total Balance,${data.summary.users.totalBalance}`,
|
||||
`Total Transactions,${data.summary.transactions.total}`,
|
||||
`Transaction Amount,${data.summary.transactions.totalAmount}`,
|
||||
`Total Billing Records,${data.summary.billing.total}`,
|
||||
`Total Revenue,${data.summary.billing.totalRevenue}`,
|
||||
`Developer Requests,${data.summary.developerRequests.total}`,
|
||||
]
|
||||
return lines.join('\n')
|
||||
}
|
||||
return 'No data available'
|
||||
}
|
||||
|
||||
if (data.length === 0) {
|
||||
return 'No data available'
|
||||
}
|
||||
|
||||
// Get headers from first object
|
||||
const headers = Object.keys(data[0]).filter(
|
||||
(key) => typeof data[0][key] !== 'object' || data[0][key] === null
|
||||
)
|
||||
|
||||
// Create CSV content
|
||||
const csvHeaders = headers.join(',')
|
||||
const csvRows = data.map((row) =>
|
||||
headers
|
||||
.map((header) => {
|
||||
const value = row[header]
|
||||
if (value === null || value === undefined) return ''
|
||||
if (typeof value === 'string' && value.includes(',')) {
|
||||
return `"${value.replace(/"/g, '""')}"`
|
||||
}
|
||||
return value
|
||||
})
|
||||
.join(',')
|
||||
)
|
||||
|
||||
return [csvHeaders, ...csvRows].join('\n')
|
||||
}
|
||||
Reference in New Issue
Block a user