This repository has been archived on 2026-06-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files

125 lines
3.5 KiB
TypeScript

/**
* GET /api/activity
*
* Workspace-wide activity feed. Aggregates recent events across all of the
* authenticated user's projects: agent sessions (builds), Coolify deployments,
* and project creation/updates.
*
* Returns ActivityItem[] shaped for the workspace Activity page.
*/
import { NextResponse } from 'next/server';
import { authSession } from '@/lib/auth/session-server';
import { query } from '@/lib/db-postgres';
export const dynamic = 'force-dynamic';
interface ActivityItem {
id: string;
projectId: string;
projectName: string;
action: string;
type: 'atlas' | 'build' | 'deploy' | 'user';
createdAt: string;
}
export async function GET() {
const session = await authSession();
if (!session?.user?.email) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const email = session.user.email;
const items: ActivityItem[] = [];
try {
// --- Agent sessions (build / deploy events) ---
const agentRows = await query<any>(
`SELECT
a.id,
a.project_id,
a.app_name,
a.status,
a.task,
a.created_at,
a.started_at,
a.completed_at,
p.data->>'productName' AS project_name,
p.data->>'name' AS project_name_fallback
FROM agent_sessions a
JOIN fs_projects p ON p.id = a.project_id
WHERE p.user_id = $1
ORDER BY a.created_at DESC
LIMIT 60`,
[email],
).catch(() => []);
for (const r of agentRows) {
const name = r.project_name || r.project_name_fallback || 'Untitled';
const status = r.status as string;
const type: ActivityItem['type'] =
status === 'completed' || status === 'failed' ? 'deploy' : 'build';
const verb =
status === 'completed'
? 'Deployed'
: status === 'failed'
? 'Deploy failed for'
: status === 'running'
? 'Building'
: 'Queued build for';
items.push({
id: `agent-${r.id}`,
projectId: r.project_id,
projectName: name,
action: `${verb} ${r.app_name || 'app'}`,
type,
createdAt: (r.completed_at || r.started_at || r.created_at).toISOString(),
});
}
// --- Project creation / significant updates ---
const projectRows = await query<any>(
`SELECT id, data, created_at, updated_at
FROM fs_projects
WHERE user_id = $1
ORDER BY created_at DESC
LIMIT 40`,
[email],
).catch(() => []);
for (const r of projectRows) {
const name = r.data?.productName || r.data?.name || 'Untitled';
items.push({
id: `project-created-${r.id}`,
projectId: r.id,
projectName: name,
action: `Created project "${name}"`,
type: 'user',
createdAt: r.created_at.toISOString(),
});
// If there's a notable status change in data, surface it
const status = r.data?.status;
if (status && status !== 'defining') {
items.push({
id: `project-status-${r.id}`,
projectId: r.id,
projectName: name,
action: `Project moved to "${status}"`,
type: 'atlas',
createdAt: r.updated_at.toISOString(),
});
}
}
// Sort all items by date descending, cap at 80
items.sort(
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
);
return NextResponse.json({ items: items.slice(0, 80) });
} catch (err) {
console.error('[/api/activity]', err);
return NextResponse.json({ items: [] });
}
}