← back to Hub
app/api/activity/route.ts
152 lines
import { NextRequest, NextResponse } from 'next/server';
import { verifyAuth } from '@/lib/auth';
import { query } from '@/lib/db';
export const dynamic = 'force-dynamic';
interface ActivityItem {
id: string;
app: string;
type: string;
title: string;
description: string;
created_at: string;
}
export async function GET(request: NextRequest) {
const user = verifyAuth(request);
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
try {
const [sdccDrafts, sdccCollabs, grantGrants, grantProposals, pattyPetitions, pattyCampaigns, freddyMatches, freddyContacts] = await Promise.all([
// SDCC: latest drafts
query('sdcc', `SELECT id, subject, status, lane, created_at FROM drafts ORDER BY created_at DESC LIMIT 5`),
// SDCC: latest collaborations
query('sdcc', `SELECT id, name, collab_type, status, created_at FROM collaborations ORDER BY created_at DESC LIMIT 5`),
// Grant: latest grants
query('grant', `SELECT id, title, funder, status, created_at FROM grants ORDER BY created_at DESC LIMIT 5`),
// Grant: latest proposals
query('grant', `SELECT id, subject, status, created_at FROM grant_proposals ORDER BY created_at DESC LIMIT 5`),
// Patty: latest petitions
query('patty', `SELECT id, title, status, signature_count, created_at FROM petitions ORDER BY created_at DESC LIMIT 5`),
// Patty: latest campaigns
query('patty', `SELECT id, subject, status, created_at FROM email_campaigns ORDER BY created_at DESC LIMIT 5`),
// Freddy: latest matches
query('freddy', `SELECT id, match_score, status, created_at FROM matches ORDER BY created_at DESC LIMIT 5`),
// Freddy: latest contacts
query('freddy', `SELECT id, name, contact_type, organization, created_at FROM contacts ORDER BY created_at DESC LIMIT 5`),
]);
const items: ActivityItem[] = [];
// SDCC drafts
for (const row of sdccDrafts.rows) {
items.push({
id: `sdcc-draft-${row.id}`,
app: 'sdcc',
type: 'draft',
title: row.subject,
description: `${row.lane} draft - ${row.status}`,
created_at: row.created_at,
});
}
// SDCC collaborations
for (const row of sdccCollabs.rows) {
items.push({
id: `sdcc-collab-${row.id}`,
app: 'sdcc',
type: 'collaboration',
title: row.name,
description: `${row.collab_type} - ${row.status}`,
created_at: row.created_at,
});
}
// Grant grants
for (const row of grantGrants.rows) {
items.push({
id: `grant-grant-${row.id}`,
app: 'grant',
type: 'grant',
title: row.title,
description: `From ${row.funder} - ${row.status}`,
created_at: row.created_at,
});
}
// Grant proposals
for (const row of grantProposals.rows) {
items.push({
id: `grant-proposal-${row.id}`,
app: 'grant',
type: 'proposal',
title: row.subject,
description: `Proposal - ${row.status}`,
created_at: row.created_at,
});
}
// Patty petitions
for (const row of pattyPetitions.rows) {
items.push({
id: `patty-petition-${row.id}`,
app: 'patty',
type: 'petition',
title: row.title,
description: `${row.signature_count || 0} signatures - ${row.status}`,
created_at: row.created_at,
});
}
// Patty campaigns
for (const row of pattyCampaigns.rows) {
items.push({
id: `patty-campaign-${row.id}`,
app: 'patty',
type: 'campaign',
title: row.subject,
description: `Email campaign - ${row.status}`,
created_at: row.created_at,
});
}
// Freddy matches
for (const row of freddyMatches.rows) {
items.push({
id: `freddy-match-${row.id}`,
app: 'freddy',
type: 'match',
title: `Match (score: ${(row.match_score * 100).toFixed(0)}%)`,
description: `Cause-Org match - ${row.status}`,
created_at: row.created_at,
});
}
// Freddy contacts
for (const row of freddyContacts.rows) {
items.push({
id: `freddy-contact-${row.id}`,
app: 'freddy',
type: 'contact',
title: row.name,
description: `${row.contact_type}${row.organization ? ' at ' + row.organization : ''}`,
created_at: row.created_at,
});
}
// Sort by created_at DESC and return top 20
items.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
return NextResponse.json({ items: items.slice(0, 20) });
} catch (err) {
console.error('[activity] Error:', (err as Error).message);
return NextResponse.json(
{ error: 'Failed to fetch activity data' },
{ status: 500 },
);
}
}