import { Elysia, t } from 'elysia'; import { db } from '../db'; import { clients } from '../db/schema'; import { eq } from 'drizzle-orm'; import { findMatches, findMatchesForClient, generateIntroSuggestion } from '../services/matching'; import type { ClientProfile, MatchResult } from '../services/matching'; function toClientProfile(c: typeof clients.$inferSelect): ClientProfile { return { id: c.id, firstName: c.firstName, lastName: c.lastName, email: c.email, company: c.company, role: c.role, industry: c.industry, city: c.city, state: c.state, interests: c.interests, tags: c.tags, notes: c.notes, }; } export const networkRoutes = new Elysia({ prefix: '/network' }) // Get all network matches for the user's clients .get('/matches', async (ctx) => { const user = (ctx as any).user; const query = ctx.query; const allClients = await db.select().from(clients).where(eq(clients.userId, user.id)); if (allClients.length < 2) { return { matches: [] as MatchResult[], total: 0, clientCount: allClients.length }; } const profiles = allClients.map(toClientProfile); const minScore = parseInt(query.minScore || '20', 10); const limit = parseInt(query.limit || '50', 10); const rawMatches = findMatches(profiles, minScore).slice(0, limit); const matches: MatchResult[] = rawMatches.map(m => ({ ...m, introSuggestion: `Consider introducing ${m.clientA.name} and ${m.clientB.name} — they share common ground.`, })); return { matches, total: matches.length, clientCount: allClients.length }; }, { query: t.Object({ minScore: t.Optional(t.String()), limit: t.Optional(t.String()), }), }) // Get matches for a specific client .get('/matches/:clientId', async (ctx) => { const user = (ctx as any).user; const params = ctx.params; const query = ctx.query; const allClients = await db.select().from(clients).where(eq(clients.userId, user.id)); const target = allClients.find(c => c.id === params.clientId); if (!target) throw new Error('Client not found'); const profiles = allClients.map(toClientProfile); const targetProfile = toClientProfile(target); const minScore = parseInt(query.minScore || '15', 10); const rawMatches = findMatchesForClient(targetProfile, profiles, minScore); const matches: MatchResult[] = rawMatches.map(m => ({ ...m, introSuggestion: `Consider introducing ${m.clientA.name} and ${m.clientB.name} — they share common ground.`, })); return { matches, client: { id: target.id, name: `${target.firstName} ${target.lastName}` } }; }, { params: t.Object({ clientId: t.String({ format: 'uuid' }), }), query: t.Object({ minScore: t.Optional(t.String()), }), }) // Generate AI intro suggestion for a specific match .post('/intro', async (ctx) => { const user = (ctx as any).user; const body = ctx.body; const allClients = await db.select().from(clients).where(eq(clients.userId, user.id)); const clientA = allClients.find(c => c.id === body.clientAId); const clientB = allClients.find(c => c.id === body.clientBId); if (!clientA || !clientB) throw new Error('Client not found'); const suggestion = await generateIntroSuggestion( toClientProfile(clientA), toClientProfile(clientB), body.reasons, (body.provider as any) || 'openai', ); return { introSuggestion: suggestion }; }, { body: t.Object({ clientAId: t.String({ format: 'uuid' }), clientBId: t.String({ format: 'uuid' }), reasons: t.Array(t.String()), provider: t.Optional(t.String()), }), }) // Network stats .get('/stats', async (ctx) => { const user = (ctx as any).user; const allClients = await db.select().from(clients).where(eq(clients.userId, user.id)); const profiles = allClients.map(toClientProfile); const matches = findMatches(profiles, 20); // Industry breakdown const industries: Record = {}; for (const c of allClients) { if (c.industry) { industries[c.industry] = (industries[c.industry] || 0) + 1; } } // Location breakdown const locations: Record = {}; for (const c of allClients) { const loc = [c.city, c.state].filter(Boolean).join(', '); if (loc) { locations[loc] = (locations[loc] || 0) + 1; } } // Category breakdown const categories: Record = {}; for (const m of matches) { categories[m.category] = (categories[m.category] || 0) + 1; } // Most connected clients const connectionCount: Record = {}; for (const m of matches) { if (!connectionCount[m.clientA.id]) connectionCount[m.clientA.id] = { name: m.clientA.name, count: 0 }; if (!connectionCount[m.clientB.id]) connectionCount[m.clientB.id] = { name: m.clientB.name, count: 0 }; connectionCount[m.clientA.id]!.count++; connectionCount[m.clientB.id]!.count++; } const topConnectors = Object.entries(connectionCount) .map(([id, { name, count }]) => ({ id, name, matchCount: count })) .sort((a, b) => b.matchCount - a.matchCount) .slice(0, 5); return { totalClients: allClients.length, totalMatches: matches.length, avgScore: matches.length > 0 ? Math.round(matches.reduce((s, m) => s + m.score, 0) / matches.length) : 0, industries: Object.entries(industries).sort((a, b) => b[1] - a[1]), locations: Object.entries(locations).sort((a, b) => b[1] - a[1]), categories, topConnectors, }; });